canvas操控像素能力的强大,也就自己试着做了一下。
只需要懂的使用canvas的getImgData()就行了。该方法能够复制画布上指定矩形的像素数据,用法很简单:
var imgData=context.getImageData(x,y,width,height);
对于 ImageData 对象中的每个像素,都存在着四方面的信息,即 RGBA 值:就酱紫就可以获取到imgData。imgData是获取到的像素信息,具体如下
R - 红色 (0-255)
G - 绿色 (0-255)
B - 蓝色 (0-255)
A - alpha 通道 (0-255; 0 是透明的,255 是完全可见的)
只要是有前端编程经验的,都肯定知道rgba了,而获取到的imgData就是一个存放着制定矩形中所有像素数组的数组,第一个像素的R是imgData[0],G是imgData[1],B是imgData[2],A则是imgData[3],第二个像素的R是imgData[4],G是imgData[5],B是imgData[6],A则是imgData[7]。。。以此类推。然后,既然我们已经获取到了所有像素里的rgba参数了,我们也就可以很简单的对这些参数进行更改,然后再将更改后的imgData通过putImageData()方法贴到画布上。像素处理完毕。就是如此简单。
知道如何获取像素数据后,接下来就可以开搞了~~~关键代码就下面这些:
function getimgData(text){ drawText(text); var imgData = context.getImageData(0,0,canvas.width , canvas.height); context.clearRect(0,0,canvas.width , canvas.height); var dots = []; for(var x=0;x<imgData.width;x+=6){ for(var y=0;y<imgData.height;y+=6){ var i = (y*imgData.width + x)*4; if(imgData.data[i+3] >= 128){ var dot = new Dot(x-3 , y-3 , 0 , 3); dots.push(dot); } } } return dots; }
为了让小圆扩散有3D的空间感,所以还引入了Z轴,也就是把3维平面化成二维,具体我就不多说了。3D效果也做好后,就做动画,随机出一个坐标,让粒子先呆在那个坐标,然后通过保存的位置再聚合,然后再随机出坐标再分散,就是这个动画的原理了。获取到imgData后,通过两个循环,获得透明度大于128的,也就是有颜色的像素,然后实例化一个粒子对象,存入一个粒子数组中保存,以下是粒子对象代码:
ar Dot = function(centerX , centerY , centerZ , radius){ this.dx = centerX; //保存原来的位置 this.dy = centerY; this.dz = centerZ; this.tx = 0; //保存粒子聚合后又飞散开的位置 this.ty = 0; this.tz = 0; this.z = centerZ; this.x = centerX; this.y = centerY; this.radius = radius; } Dot.prototype = { paint:function(){ context.save(); context.beginPath(); var scale = focallength/(focallength + this.z); context.arc(canvas.width/2 + (this.x-canvas.width/2)*scale , canvas.height/2 + (this.y-canvas.height/2) * scale, this.radius*scale , 0 , 2*Math.PI); context.fillStyle = "rgba(50,50,50,"+ scale +")"; context.fill() context.restore(); } }
下面贴出所有代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <style> #cas{ display: block; border:1px solid; margin:auto; } </style> <script> window.onload = function(){ canvas = document.getElementById("cas"); context = canvas.getContext('2d'); focallength = 250; var dots = getimgData(document.getElementById('name').value);; var pause = false; initAnimate(); function initAnimate(){ dots.forEach(function(){ this.x = Math.random()*canvas.width; this.y = Math.random()*canvas.height; this.z = Math.random()*focallength*2 - focallength; this.tx = Math.random()*canvas.width; this.ty = Math.random()*canvas.height; this.tz = Math.random()*focallength*2 - focallength; this.paint(); }); animate(); } //计算帧速率 var lastTime; var derection = true; function animate(){ animateRunning = true; var thisTime = +new Date(); context.clearRect(0,0,canvas.width , canvas.height); dots.forEach(function(){ var dot = this; if(derection){ if (Math.abs(dot.dx - dot.x) < 0.1 && Math.abs(dot.dy - dot.y) < 0.1 && Math.abs(dot.dz - dot.z)<0.1) { dot.x = dot.dx; dot.y = dot.dy; dot.z = dot.dz; if(thisTime - lastTime > 300) derection = false; } else { dot.x = dot.x + (dot.dx - dot.x) * 0.1; dot.y = dot.y + (dot.dy - dot.y) * 0.1; dot.z = dot.z + (dot.dz - dot.z) * 0.1; lastTime = +new Date() } } else { if (Math.abs(dot.tx - dot.x) < 0.1 && Math.abs(dot.ty - dot.y) < 0.1 && Math.abs(dot.tz - dot.z)<0.1) { dot.x = dot.tx; dot.y = dot.ty; dot.z = dot.tz; pause = true; } else { dot.x = dot.x + (dot.tx - dot.x) * 0.1; dot.y = dot.y + (dot.ty - dot.y) * 0.1; dot.z = dot.z + (dot.tz - dot.z) * 0.1; pause = false; } } dot.paint(); }); if(!pause) { if("requestAnimationFrame" in window){ requestAnimationFrame(animate); } else if("webkitRequestAnimationFrame" in window){ webkitRequestAnimationFrame(animate); } else if("msRequestAnimationFrame" in window){ msRequestAnimationFrame(animate); } else if("mozRequestAnimationFrame" in window){ mozRequestAnimationFrame(animate); } } } document.getElementById('startBtn').onclick = function(){ if(!pause) return; dots = getimgData(document.getElementById('name').value); derection=true; pause = false; initAnimate(); } } Array.prototype.forEach = function(callback){ for(var i=0;i<this.length;i++){ callback.call(this[i]); } } function getimgData(text){ drawText(text); var imgData = context.getImageData(0,0,canvas.width , canvas.height); context.clearRect(0,0,canvas.width , canvas.height); var dots = []; for(var x=0;x<imgData.width;x+=6){ for(var y=0;y<imgData.height;y+=6){ var i = (y*imgData.width + x)*4; if(imgData.data[i] >= 128){ var dot = new Dot(x-3 , y-3 , 0 , 3); dots.push(dot); } } } return dots; } function drawText(text){ context.save() context.font = "200px 微软雅黑 bold"; context.fillStyle = "rgba(168,168,168,1)"; context.textAlign = "center"; context.textBaseline = "middle"; context.fillText(text , canvas.width/2 , canvas.height/2); context.restore(); } var Dot = function(centerX , centerY , centerZ , radius){ this.dx = centerX; this.dy = centerY; this.dz = centerZ; this.tx = 0; this.ty = 0; this.tz = 0; this.z = centerZ; this.x = centerX; this.y = centerY; this.radius = radius; } Dot.prototype = { paint:function(){ context.save(); context.beginPath(); var scale = focallength/(focallength + this.z); context.arc(canvas.width/2 + (this.x-canvas.width/2)*scale , canvas.height/2 + (this.y-canvas.height/2) * scale, this.radius*scale , 0 , 2*Math.PI); context.fillStyle = "rgba(50,50,50,"+ scale +")"; context.fill() context.restore(); } } </script> <title>操控字体的数据</title> </head> <body> <div > <canvas id='cas' width="1000" height="500">浏览器不支持canvas</canvas> <div style="width:150px;margin:10px auto"> <input type="text" name="" id="name" style="width:80px;" value="王鸿兴"><button id="startBtn">开始</button> </div> </div> </body> </html>
技术不是很好,代码写的不好请见谅。不过应该不难理解。
知道文字如何粒子化后,图片也就一样的做法了。这里是另一个demo
补充一点:过于复杂的图片不适合粒子化,因为对象过多时,对浏览器的造成很大的负荷,动画效果也就会变得很卡很卡了。一般一千个粒子左右比较合适,自己就根据图片复杂度调整粒子的大小,减少粒子数量就行了。