隨便談談用canvas來實現文字圖片粒子化


  聲明:本文為原創文章,如需轉載,請注明來源WAxes,謝謝!

  看了岑安大大的教程http://www.cnblogs.com/hongru/archive/2012/03/28/2420415.html后,讓我見識到了canvas操控像素能力的強大,也就自己試着做了一下。發現如此好玩的效果也正如岑安大大所說的一樣,事情沒有想象中那么難。

  先看個DEMO吧,先從文字下手:文字粒子化

  要做出這樣的效果,只需要懂的使用canvas的getImgData()就行了。該方法能夠復制畫布上指定矩形的像素數據,用法很簡單:

var imgData=context.getImageData(x,y,width,height);

就醬紫就可以獲取到imgData。imgData是獲取到的像素信息,具體如下

對於 ImageData 對象中的每個像素,都存在着四方面的信息,即 RGBA 值:

  • 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;
}

獲取到imgData后,通過兩個循環,獲得透明度大於128的,也就是有顏色的像素,然后實例化一個粒子對象,存入一個粒子數組中保存,以下是粒子對象代碼:

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();
			}
		}

為了讓小圓擴散有3D的空間感,所以還引入了Z軸,也就是把3維平面化成二維,具體我就不多說了。可以直接戳http://www.cnblogs.com/hongru/archive/2011/09/12/2174187.html   我就是看着岑安大大的教程學的。3D效果也做好后,就做動畫,隨機出一個坐標,讓粒子先呆在那個坐標,然后通過保存的位置再聚合,然后再隨機出坐標再分散,就是這個動畫的原理了。

  下面貼出所有代碼:

  1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  3 <head>
  4     <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
  5     <style>
  6         #cas{
  7             display: block;
  8             border:1px solid;
  9             margin:auto;
 10         }
 11     </style>
 12     <script>
 13         window.onload = function(){
 14             canvas = document.getElementById("cas");
 15             context = canvas.getContext('2d');
 16             focallength = 250;
 17 
 18             var dots = getimgData(document.getElementById('name').value);;
 19             var pause = false;
 20             initAnimate();
 21             function initAnimate(){
 22                 dots.forEach(function(){
 23                     this.x = Math.random()*canvas.width;
 24                     this.y = Math.random()*canvas.height;
 25                     this.z = Math.random()*focallength*2 - focallength;
 26 
 27                     this.tx = Math.random()*canvas.width;
 28                     this.ty = Math.random()*canvas.height;
 29                     this.tz = Math.random()*focallength*2 - focallength;
 30                     this.paint();
 31                 });
 32                 animate();
 33             }
 34 
 35             //計算幀速率
 36             var lastTime;
 37             var derection = true;
 38             function animate(){
 39                 animateRunning = true;
 40                 var thisTime = +new Date();
 41                 context.clearRect(0,0,canvas.width , canvas.height);
 42                 dots.forEach(function(){
 43                     var dot = this;
 44                     if(derection){
 45                         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) {
 46                             dot.x = dot.dx;
 47                             dot.y = dot.dy;
 48                             dot.z = dot.dz;
 49                             if(thisTime - lastTime > 300) derection = false;
 50                         } else {
 51                             dot.x = dot.x + (dot.dx - dot.x) * 0.1;
 52                             dot.y = dot.y + (dot.dy - dot.y) * 0.1;
 53                             dot.z = dot.z + (dot.dz - dot.z) * 0.1;
 54                             lastTime = +new Date()
 55                         }
 56                     }
 57                     else {
 58                         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) {
 59                             dot.x = dot.tx;
 60                             dot.y = dot.ty;
 61                             dot.z = dot.tz;
 62                             pause = true;
 63                         } else {
 64                             dot.x = dot.x + (dot.tx - dot.x) * 0.1;
 65                             dot.y = dot.y + (dot.ty - dot.y) * 0.1;
 66                             dot.z = dot.z + (dot.tz - dot.z) * 0.1;
 67                             pause = false;
 68                         }
 69                     }
 70                     dot.paint();
 71                 });
 72                 if(!pause) {
 73                     if("requestAnimationFrame" in window){
 74                         requestAnimationFrame(animate);
 75                     }
 76                     else if("webkitRequestAnimationFrame" in window){
 77                         webkitRequestAnimationFrame(animate);
 78                     }
 79                     else if("msRequestAnimationFrame" in window){
 80                         msRequestAnimationFrame(animate);
 81                     }
 82                     else if("mozRequestAnimationFrame" in window){
 83                         mozRequestAnimationFrame(animate);
 84                     }
 85                 }
 86             }
 87 
 88             document.getElementById('startBtn').onclick = function(){
 89                 if(!pause) return;
 90                 dots = getimgData(document.getElementById('name').value);
 91                 derection=true;
 92                 pause = false;
 93                 initAnimate();
 94             }
 95         }
 96 
 97         Array.prototype.forEach = function(callback){
 98             for(var i=0;i<this.length;i++){
 99                 callback.call(this[i]);
100             }
101         }
102 
103         function getimgData(text){
104             drawText(text);
105             var imgData = context.getImageData(0,0,canvas.width , canvas.height);
106             context.clearRect(0,0,canvas.width , canvas.height);
107             var dots = [];
108             for(var x=0;x<imgData.width;x+=6){
109                 for(var y=0;y<imgData.height;y+=6){
110                     var i = (y*imgData.width + x)*4;
111                     if(imgData.data[i] >= 128){
112                         var dot = new Dot(x-3 , y-3 , 0 , 3);
113                         dots.push(dot);
114                     }
115                 }
116             }
117             return dots;
118         }
119 
120         function drawText(text){
121             context.save()
122             context.font = "200px 微軟雅黑 bold";
123             context.fillStyle = "rgba(168,168,168,1)";
124             context.textAlign = "center";
125             context.textBaseline = "middle";
126             context.fillText(text , canvas.width/2 , canvas.height/2);
127             context.restore();
128         }
129 
130 
131         var Dot = function(centerX , centerY , centerZ , radius){
132             this.dx = centerX;
133             this.dy = centerY;
134             this.dz = centerZ;
135             this.tx = 0;
136             this.ty = 0;
137             this.tz = 0;
138             this.z = centerZ;
139             this.x = centerX;
140             this.y = centerY;
141             this.radius = radius;
142         }
143 
144         Dot.prototype = {
145             paint:function(){
146                 context.save();
147                 context.beginPath();
148                 var scale = focallength/(focallength + this.z);
149                 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);
150                 context.fillStyle = "rgba(50,50,50,"+ scale +")";
151                 context.fill()
152                 context.restore();
153             }
154         }
155     </script>
156     <title>操控字體的數據</title>
157 </head>
158 <body>
159     <div >
160         <canvas id='cas' width="1000" height="500">瀏覽器不支持canvas</canvas>
161         <div style="width:150px;margin:10px auto">
162             <input type="text" name="" id="name" style="width:80px;" value="王鴻興"><button id="startBtn">開始</button>
163         </div>
164     </div>
165 </body>
166 </html>

技術不是很好,代碼寫的不好請見諒。不過應該不難理解。

  知道文字如何粒子化后,圖片也就一樣的做法了。這里是另一個demo 粒子化Demo1

  源碼地址:https://github.com/whxaxes/canvas-test/tree/gh-pages/src/Particle-demo/imgdata

  補充一點:過於復雜的圖片不適合粒子化,因為對象過多時,對瀏覽器的造成很大的負荷,動畫效果也就會變得很卡很卡了。一般一千個粒子左右比較合適,自己就根據圖片復雜度調整粒子的大小,減少粒子數量就行了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM