Ефект Pixel Rain над зображенням у полотні canvas

Публікації

Ефект Pixel Rain (піксельний дощ) над зображенням дозволяє краплям дощу малювати контори зображення на полотні canvas.

Малювання дощу відбувається шляхом малювання фону (чорний фон) з прозорістю globalAlpha = 0.05 і малювання крапель дощу з прозорістю 0.2.

var canvas1=document.getElementById('canva1'); var ctx1=canvas1.getContext('2d'); //масив дощових крапель var masPixRain=[]; for(let i=0;i<100;i++){ masPixRain.push({x:Math.random()*canvas1.width, y:0, sped:Math.random()+0.5, size:Math.random()*1.5}); } //функція анімації function anime1(){ ctx1.globalAlpha=0.05; ctx1.fillStyle='rgb(0,0,0)'; ctx1.fillRect(0, 0, canvas1.width, canvas1.height); for(let i=0;i<masPixRain.length;i++){ masPixRain[i].y+=masPixRain[i].sped; if(masPixRain[i].y>canvas1.height){ masPixRain[i].y=0; masPixRain[i].x=Math.random()*canvas1.width; } ctx1.beginPath(); ctx1.fillStyle='white'; ctx1.globalAlpha=0.2; ctx1.arc(masPixRain[i].x, masPixRain[i].y, masPixRain[i].size,0,Math.PI*2); ctx1.fill(); } requestAnimationFrame(anime1); } anime1();

Замість чорного фону можна малювати зображення.

var img=new Image(); var canvas2=document.getElementById('canva2'); var ctx2=canvas2.getContext('2d'); //масив дощових крапель var masPixRain2=[]; for(let i=0;i<100;i++){ masPixRain2.push({x:Math.random()*canvas2.width, y:0, sped:Math.random()+0.5, size:Math.random()*1.5}); } //функція анімації function anime2(){ ctx2.globalAlpha=0.2; ctx2.drawImage(img, 0, 0, canvas2.width, canvas2.height); for(let i=0;i<masPixRain2.length;i++){ masPixRain2[i].y+=masPixRain2[i].sped; if(masPixRain2[i].y>canvas2.height){ masPixRain2[i].y=0; masPixRain2[i].x=Math.random()*canvas2.width; } ctx2.beginPath(); ctx2.fillStyle='white'; ctx2.globalAlpha=0.5; ctx2.arc(masPixRain2[i].x, masPixRain2[i].y, masPixRain2[i].size,0,Math.PI*2); ctx2.fill(); } requestAnimationFrame(anime2); } img.src='/dani/test.png'; img.onload=function(){ anime2(); }

Для більш кращого ефекту Pixel Rain на зображені будемо відображати краплі на певному діапазоні кольлорів. Тобто чи щось намальовано на зображенні чи це "фон".

Отримуємо дані кольорів пікселя за допомогою getImageData(). З масиву даних ImageData отримуємо дані R G B (Red Green Blue) пікселя. Перевіряємо якщо R G B на необхідний діапазон, наприклад більше 100 тоді малюємо краплі. Перевіряти можна як R G B окремо так і їх загальну суму і т.п.

//чи R G B більше за значення if(r>100 && g>100 && b>100){..малюємо краплі на полотні..} //сума R G B більша за значення if(r+g+b>150){...} //чорне або біле, визначаємо відтінок середнім значенням RGB if((r+g+b)/3<112){...} //чи R G B входить у діапазон, наприклад R <75 && R>125 let spectrColor=25; if(r>100-spectrColor && r<100+spectrColor) && (g>110-spectrColor && g<110+spectrColor) && (b>90-spectrColor && b<90+spectrColor){...} var img3=new Image(); var pixData3=[]; var canvas3=document.getElementById('canva3'); var ctx3=canvas3.getContext('2d'); //масив дощових крапель var masPixRain3=[]; for(let i=0;i<500;i++){ masPixRain3.push({x:Math.random()*canvas3.width, y:0, sped:Math.random()+0.5, size:Math.random()*1.5}); } //функція анімації function anime3(){ ctx3.globalAlpha=0.05; ctx3.fillStyle='rgb(0,0,0)'; ctx3.fillRect(0, 0, canvas3.width, canvas3.height); for(let i=0;i<masPixRain3.length;i++){ masPixRain3[i].y+=masPixRain3[i].sped; if(masPixRain3[i].y>canvas3.height){ masPixRain3[i].y=0; masPixRain3[i].x=Math.random()*canvas3.width; } //отримуємо R G B пікселя зображення з масиву let c=pixData3[parseInt(masPixRain3[i].y)][parseInt(masPixRain3[i].x)]; //перевірка пікселя на діапазон if(c.r>100 && c.g>100 && c.b>100){ ctx3.beginPath(); ctx3.fillStyle='white'; ctx3.globalAlpha=0.2; ctx3.arc(masPixRain3[i].x, masPixRain3[i].y, masPixRain3[i].size,0,Math.PI*2); ctx3.fill(); } } requestAnimationFrame(anime3); } img3.src="/dani/test.png"; img3.onload=function(){ //малюємо зображення ctx3.drawImage(img3, 0,0,canvas3.width, canvas3.height); //отримуємо дані пікселя let imgData=ctx3.getImageData(0,0,canvas3.width,canvas3.height); //записуємо масив дані кольрів пікселя for(let y=0;y<canvas3.height;y++){ let row=[]; for(let x=0,r,g,b;x<canvas3.width;x++){ r=imgData.data[(y*4*imgData.width)+(x*4)]; g=imgData.data[(y*4*imgData.width)+(x*4+1)]; b=imgData.data[(y*4*imgData.width)+(x*4+2)]; row.push({r:r,g:g,b:b}); } pixData3.push(row); } anime3(); }

Клас PixelRaint який дозволяє налаштовувати діапазон RGB пікселів в якому будуть відображатися краплі дощу.

Конструктор класу приймає параметри canvas - css селектор елемента canvas, src - URL адреса зображення, red - значення червоного кольору пікселя для діапазону від 0 до 255,green - значення зеленого кольору пікселя для діапазону від 0 до 255, blue - значення червоного кольору пікселя для діапазону від 0 до 255.

По замовчуванню перевірка діапазонує : r<255 && g<255 && b<255.

Клас має властивості lmRed, lmGreen, lmBlue які дозволяють вказувати порівняння кольору пікселля більше або менше. Наприклад lmRed=true це red більше за вказане значення, а якщо lmRed=false то red менше за вказане значення.

Це дозволяє налаштувати ефект PixelRaint для кожного зображення індивідуально.

class PixelRaint{ #img=new Image(); #ctx; #maps=[]; #particles=[]; #count=500; //функція яка перевіряє пікесь на діапазон, чи малювати краплю чи ні #isDrawPix(r,g,b){ return (this.lmRed?r>this.red:r<this.red) && (this.lmGreen?g>this.green:g<this.green) && (this.lmBlue?b>this.blue:b<this.blue); } #getX(){ return Math.random()*this.canvas.width; } get src(){ return this.#img.src; } set src(url){ this.#particles.length=0; this.#maps.length=0; this.#img.src=url; } constructor(canvas,src,red=255,green=255,blue=255){ this.canvas=document.querySelector(canvas); this.#ctx=this.canvas.getContext('2d'); this.red=red; this.lmRed=false; this.green=green; this.lmGreen=false; this.blue=blue; this.lmBlue=false; this.#img.src=src; this.#img.onload=function(){ this.canvas.width=this.#img.naturalWidth; this.canvas.height=this.#img.naturalHeight; if(this.canvas.width>520)this.canvas.width=520; if(this.canvas.height>480)this.canvas.height=480; this.#ctx.drawImage(this.#img,0,0,this.canvas.width,this.canvas.height); let imgData=this.#ctx.getImageData(0,0,this.canvas.width,this.canvas.height); for(let y=0;y<this.canvas.height;y++){ let row=[]; for(let x=0,r,g,b;x<this.canvas.width;x++){ r=imgData.data[(y*4*imgData.width)+(x*4)]; g=imgData.data[(y*4*imgData.width)+(x*4+1)]; b=imgData.data[(y*4*imgData.width)+(x*4+2)]; row.push({r:r,g:g,b:b}); } this.#maps.push(row); } for(var i=0;i<this.#count;i++){ this.#particles.push({ x:this.#getX(), y:0, sped:Math.random()*(2-0.25)+0.25, alpha: Math.random()*(1-0.35)+0.35, size: Math.random()*1.5 }); } this.anime(); }.bind(this); } update(index){ this.#particles[index].y+=this.#particles[index].sped; if(this.#particles[index].y>this.canvas.height){ this.#particles[index].y=0; this.#particles[index].x=this.#getX(); } let c=this.#maps[parseInt(this.#particles[index].y)][parseInt(this.#particles[index].x)]; if(this.#isDrawPix(c.r,c.g,c.b)){ //draw this.#ctx.beginPath(); this.#ctx.fillStyle='white'; this.#ctx.globalAlpha=this.#particles[index].alpha; this.#ctx.arc(this.#particles[index].x,this.#particles[index].y,this.#particles[index].size,0,Math.PI*2); this.#ctx.fill(); } } anime(){ this.#ctx.globalAlpha=0.05; this.#ctx.fillStyle='rgb(0,0,0)'; this.#ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); for(let i=0;i<this.#particles.length;i++){ this.update(i); } requestAnimationFrame(this.anime.bind(this)); } } var ob=new PixelRaint('#canva','/dani/test.png');
2023-10-31 22:49:56

Тільки зареєстровані користувачі можуть писати коментарі.