亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

基于canvas粒子系統(tǒng)的構(gòu)建詳解

 更新時間:2017年08月31日 09:30:07   作者:小火柴的藍(lán)色理想  
下面小編就為大家?guī)硪黄赾anvas粒子系統(tǒng)的構(gòu)建詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

前面的話

本文將從最基本的imageData對象的理論知識說開去,詳細(xì)介紹canvas粒子系統(tǒng)的構(gòu)建

imageData

關(guān)于圖像數(shù)據(jù)imageData共有3個方法,包括getImageData()、putImageData()、createImageData()

【getImageData()】

2D上下文可以通過getImageData()取得原始圖像數(shù)據(jù)。這個方法接收4個參數(shù):畫面區(qū)域的x和y坐標(biāo)以及該區(qū)域的像素寬度和高度

例如,要取得左上角坐標(biāo)為(10,5)、大小為50*50像素的區(qū)域的圖像數(shù)據(jù),可以使用以下代碼:

var imageData = context.getImageData(10,5,50,50);

返回的對象是ImageData的實例,每個ImageData對象有3個屬性:width\height\data

1、width:表示imageData對角的寬度

2、height:表示imageData對象的高度

3、data是一個數(shù)組,保存著圖像中每一個像素的數(shù)據(jù)。在data數(shù)組中,每一個像素用4個元素來保存,分別表示red、green、blue、透明度

[注意]圖像中有多少像素,data的長度就等于像素個數(shù)乘以4

//第一個像素如下
var data = imageData.data;
var red = data[0];
var green = data[1]; 
var blue = data[2];
var alpha = data[3];

數(shù)組中每個元素的值是在0-255之間,能夠直接訪問到原始圖像數(shù)據(jù),就能夠以各種方式來操作這些數(shù)據(jù)

[注意]如果要使用getImageData()獲取的canvas中包含drawImage()方法,則該方法中的URL不能跨域

【createImageData()】

createImageData(width,height)方法創(chuàng)建新的空白ImageData對象。新對象的默認(rèn)像素值 transparent black,相當(dāng)于rgba(0,0,0,0)

var imgData = context.createImageData(100,100);

【putImageData()】

putImageData()方法將圖像數(shù)據(jù)從指定的ImageData對象放回畫布上,該方法共有以下參數(shù)

imgData:要放回畫布的ImageData對象(必須)
x:imageData對象的左上角的x坐標(biāo)(必須)
y:imageData對象的左上角的y坐標(biāo)(必須)
dirtyX:在畫布上放置圖像的水平位置(可選)
dirtyY:在畫布上放置圖像的垂直位置(可選)
dirtyWidth:在畫布上繪制圖像所使用的寬度(可選)
dirtyHeight:在畫布上繪制圖像所使用的高度(可選)

[注意]參數(shù)3到7要么都沒有,要么都存在

context.putImageData(imgData,0,0);
context.putImageData(imgData,0,0,50,50,200,200);

粒子寫入

粒子,指圖像數(shù)據(jù)imageData中的每一個像素點。下面以一個簡易實例來說明完全寫入與粒子寫入

【完全寫入】

200*200的canvas1中存在文字'小火柴',并將canvas1整個作為圖像數(shù)據(jù)寫入同樣尺寸的canvas2中

<canvas id="drawing1" style="border:1px solid black"></canvas>
<canvas id="drawing2" style="border:1px solid black"></canvas>
<script>
var drawing1 = document.getElementById('drawing1');
var drawing2 = document.getElementById('drawing2');
if(drawing1.getContext){
 var cxt = drawing1.getContext('2d');
 var cxt2 = drawing2.getContext('2d');
 var W = drawing1.width = drawing2.width = 200;
 var H = drawing1.height = drawing2.height = 200;
 var str = '小火柴';
 cxt.textBaseline = 'top';
 var sh = 60;
 cxt.font = sh + 'px 宋體'
 var sw = cxt.measureText(str).width;
 if(sw > W){
  sw = W;
 }
 cxt.fillText(str,(W - sw)/2,(H - sh)/2,W);
 //獲取imageData
 var imageData = cxt.getImageData(0,0,W,H); 
 //寫入drawing2中 
 cxt2.putImageData(imageData,0,0);
</script>

【粒子寫入】

對于完全寫入而言,相當(dāng)于只是簡單的復(fù)制粘貼,如果要對每個像素點進(jìn)行精細(xì)地控制,則需要使用粒子寫入。canvas1中存在著大量的空白區(qū)域,只有'小火柴'這三個字的區(qū)域是有效的。于是,可以根據(jù)圖像數(shù)據(jù)imageData中的透明度對粒子進(jìn)行篩選,只篩選出透明度大于0的粒子

<canvas id="drawing1" style="border:1px solid black"></canvas>
<canvas id="drawing2" style="border:1px solid black"></canvas>
<script>
var drawing1 = document.getElementById('drawing1');
var drawing2 = document.getElementById('drawing2');
if(drawing1.getContext){
 var cxt = drawing1.getContext('2d');
 var cxt2 = drawing2.getContext('2d');
 var W = drawing1.width = drawing2.width = 200;
 var H = drawing1.height = drawing2.height = 200;
 var str = '小火柴';
 cxt.textBaseline = 'top';
 var sh = 60;
 cxt.font = sh + 'px 宋體'
 var sw = cxt.measureText(str).width;
 if(sw > W){
  sw = W;
 }
 cxt.fillText(str,(W - sw)/2,(H - sh)/2,W);
 //獲取imageData
 var imageData = cxt.getImageData(0,0,W,H); 
 //寫入drawing2中 
 cxt2.putImageData(setData(imageData),0,0);
 function setData(imageData){
 //從imageData對象中取得粒子,并存儲到dots數(shù)組中
 var dots = [];
 for(var i = 0; i < W; i++){
  for(var j = 0; j < H ;j++){
  //data值中的紅色值
  var k = 4*(i + j*W);
  //data值中的透明度
  if(imageData.data[k+3] > 0){
   //將透明度大于0的data中的紅色值保存到dots數(shù)組中
   dots.push(k);
  }
  }
 }
 //40000 2336
 console.log(i*j,dots.length);
 //新建一個imageData,并將篩選后的粒子信息保存到新建的imageData中
 var oNewImage = cxt.createImageData(W,H);
 for(var i = 0; i < dots.length; i++){
  oNewImage.data[dots[i]+0] = imageData.data[dots[i]+0];
  oNewImage.data[dots[i]+1] = imageData.data[dots[i]+1];
  oNewImage.data[dots[i]+2] = imageData.data[dots[i]+2];
  oNewImage.data[dots[i]+3] = imageData.data[dots[i]+3];
 }
 return oNewImage;
 }
}
</script>

雖然結(jié)果看上去相同,但canvas2只使用了canvas1中40000個粒子中的2336個

粒子篩選

當(dāng)粒子完全寫入時,與canvas復(fù)制粘貼的效果相同。而當(dāng)粒子有所篩選時,則會出現(xiàn)一些奇妙的效果

【按序篩選】

由于取得粒子時,使用的是寬度值*高度值的雙重循環(huán),且都以加1的形式遞增。如果不是加1,而是加n,則可以實現(xiàn)按序篩選的效果

<canvas id="drawing1" style="border:1px solid black"></canvas>
<canvas id="drawing2" style="border:1px solid black"></canvas>
<div id="con">
 <button>1</button>
 <button>2</button>
 <button>3</button>
 <button>4</button>
 <button>5</button>
</div>
<script>
var oCon = document.getElementById('con');
oCon.onclick = function(e){
 e = e || event;
 var tempN = e.target.innerHTML;
 if(tempN){
 cxt2.clearRect(0,0,W,H);
 cxt2.putImageData(setData(imageData,Number(tempN)),0,0);
 }
}
var drawing1 = document.getElementById('drawing1');
var drawing2 = document.getElementById('drawing2');
if(drawing1.getContext){
 var cxt = drawing1.getContext('2d');
 var cxt2 = drawing2.getContext('2d');
 var W = drawing1.width = drawing2.width = 200;
 var H = drawing1.height = drawing2.height = 200;
 var str = '小火柴';
 cxt.textBaseline = 'top';
 var sh = 60;
 cxt.font = sh + 'px 宋體'
 var sw = cxt.measureText(str).width;
 if(sw > W){
  sw = W;
 }
 cxt.fillText(str,(W - sw)/2,(H - sh)/2,W);
 //獲取imageData
 var imageData = cxt.getImageData(0,0,W,H); 
 //寫入drawing2中 
 cxt2.putImageData(setData(imageData,1),0,0);
 function setData(imageData,n){
 //從imageData對象中取得粒子,并存儲到dots數(shù)組中
 var dots = [];
 for(var i = 0; i < W; i+=n){
  for(var j = 0; j < H ;j+=n){
  //data值中的紅色值
  var k = 4*(i + j*W);
  //data值中的透明度
  if(imageData.data[k+3] > 0){
   //將透明度大于0的data中的紅色值保存到dots數(shù)組中
   dots.push(k);
  }
  }
 }
 //新建一個imageData,并將篩選后的粒子信息保存到新建的imageData中
 var oNewImage = cxt.createImageData(W,H);
 for(var i = 0; i < dots.length; i++){
  oNewImage.data[dots[i]+0] = imageData.data[dots[i]+0];
  oNewImage.data[dots[i]+1] = imageData.data[dots[i]+1];
  oNewImage.data[dots[i]+2] = imageData.data[dots[i]+2];
  oNewImage.data[dots[i]+3] = imageData.data[dots[i]+3];
 }
 return oNewImage;
 }
}
</script>

【隨機(jī)篩選】

除了使用按序篩選,還可以使用隨機(jī)篩選。 通過雙重循環(huán)得到的粒子的位置信息,放到dots數(shù)組中。通過splice()方法進(jìn)行篩選,將篩選后的位置信息放到新建的newDots數(shù)組中,然后再使用createImageData(),新建一個圖像數(shù)據(jù)對象并返回

<canvas id="drawing1" style="border:1px solid black"></canvas>
<canvas id="drawing2" style="border:1px solid black"></canvas>
<div id="con">
 <button>1000</button>
 <button>2000</button>
 <button>3000</button>
 <button>4000</button>
</div>
<script>
var oCon = document.getElementById('con');
oCon.onclick = function(e){
 e = e || event;
 var tempN = e.target.innerHTML;
 if(tempN){
 cxt2.clearRect(0,0,W,H);
 cxt2.putImageData(setData(imageData,1,Number(tempN)),0,0);
 }
}
var drawing1 = document.getElementById('drawing1');
var drawing2 = document.getElementById('drawing2');
if(drawing1.getContext){
 var cxt = drawing1.getContext('2d');
 var cxt2 = drawing2.getContext('2d');
 var W = drawing1.width = drawing2.width = 200;
 var H = drawing1.height = drawing2.height = 200;
 var str = '小火柴';
 cxt.textBaseline = 'top';
 var sh = 60;
 cxt.font = sh + 'px 宋體'
 var sw = cxt.measureText(str).width;
 if(sw > W){
  sw = W;
 }
 cxt.fillText(str,(W - sw)/2,(H - sh)/2,W);
 //獲取imageData
 var imageData = cxt.getImageData(0,0,W,H); 
 //寫入drawing2中 
 cxt2.putImageData(setData(imageData,1),0,0);
 function setData(imageData,n,m){
 //從imageData對象中取得粒子,并存儲到dots數(shù)組中
 var dots = [];
 for(var i = 0; i < W; i+=n){
  for(var j = 0; j < H ;j+=n){
  //data值中的紅色值
  var k = 4*(i + j*W);
  //data值中的透明度
  if(imageData.data[k+3] > 0){
   //將透明度大于0的data中的紅色值保存到dots數(shù)組中
   dots.push(k);
  }
  }
 } 
 //篩選粒子,僅保存m個到newDots數(shù)組中。如果不傳入m,則不進(jìn)行篩選
 var newDots = [];
 if(m && (dots.length > m)){
  for(var i = 0; i < m; i++){
  newDots.push(Number(dots.splice(Math.floor(Math.random()*dots.length),1)));
  }
 }else{
  newDots = dots;
 } 
 //新建一個imageData,并將篩選后的粒子信息保存到新建的imageData中
 var oNewImage = cxt.createImageData(W,H);
 for(var i = 0; i < newDots.length; i++){
  oNewImage.data[newDots[i]+0] = imageData.data[newDots[i]+0];
  oNewImage.data[newDots[i]+1] = imageData.data[newDots[i]+1];
  oNewImage.data[newDots[i]+2] = imageData.data[newDots[i]+2];
  oNewImage.data[newDots[i]+3] = imageData.data[newDots[i]+3];
 }
 return oNewImage;
 }
}
</script>

像素顯字

下面來使用粒子篩選來實現(xiàn)一個像素顯字的效果。像素顯字即從不清晰的效果逐步過渡到完全顯示

【按序像素顯字】

按序像素顯字的實現(xiàn)原理非常簡單,比如,共有2000個粒子,共10個程度的過渡效果。則使用10個數(shù)組,分別保存200,400,600,800,100,1200,1400,1600,1800和2000個粒子。然后使用定時器將其逐步顯示出來即可

<canvas id="drawing1" style="border:1px solid black"></canvas>
<button id="btn">開始顯字</button>
<script>
var drawing1 = document.getElementById('drawing1');
if(drawing1.getContext){
 var cxt = drawing1.getContext('2d');
 var W = drawing1.width = 200;
 var H = drawing1.height = 200;
 var str = '小火柴';
 cxt.textBaseline = 'top';
 var sh = 60;
 cxt.font = sh + 'px 宋體'
 var sw = cxt.measureText(str).width;
 if(sw > W){
  sw = W;
 }
 cxt.fillText(str,(W - sw)/2,(H - sh)/2,W);
 //獲取imageData
 var imageData = cxt.getImageData(0,0,W,H); 
 cxt.clearRect(0,0,W,H);
 //獲得10組粒子
 var imageDataArr = [];
 var n = 10;
 var index = 0;
 for(var i = n; i > 0; i--){
 imageDataArr.push(setData(imageData,i));
 }
 var oTimer = null;
 btn.onclick = function(){
 clearTimeout(oTimer);
 showData();
 }
 function showData(){
 oTimer = setTimeout(function(){
  cxt.clearRect(0,0,W,H);
  //寫入drawing1中 
  cxt.putImageData(imageDataArr[index++],0,0); 
  //迭代函數(shù)  
  showData();  
  if(index == 10){
    index = 0;
  clearTimeout(oTimer);
  }  

 },100);  
 } 
 function setData(imageData,n,m){
 //從imageData對象中取得粒子,并存儲到dots數(shù)組中
 var dots = [];
 for(var i = 0; i < W; i+=n){
  for(var j = 0; j < H ;j+=n){
  //data值中的紅色值
  var k = 4*(i + j*W);
  //data值中的透明度
  if(imageData.data[k+3] > 0){
   //將透明度大于0的data中的紅色值保存到dots數(shù)組中
   dots.push(k);
  }
  }
 } 
 //篩選粒子,僅保存m個到newDots數(shù)組中。如果不傳入m,則不進(jìn)行篩選
 var newDots = [];
 if(m && (dots.length > m)){
  for(var i = 0; i < m; i++){
  newDots.push(Number(dots.splice(Math.floor(Math.random()*dots.length),1)));
  }
 }else{
  newDots = dots;
 } 
 //新建一個imageData,并將篩選后的粒子信息保存到新建的imageData中
 var oNewImage = cxt.createImageData(W,H);
 for(var i = 0; i < newDots.length; i++){
  oNewImage.data[newDots[i]+0] = imageData.data[newDots[i]+0];
  oNewImage.data[newDots[i]+1] = imageData.data[newDots[i]+1];
  oNewImage.data[newDots[i]+2] = imageData.data[newDots[i]+2];
  oNewImage.data[newDots[i]+3] = imageData.data[newDots[i]+3];
 }
 return oNewImage;
 }
}
</script>

【隨機(jī)像素顯字】

隨機(jī)像素顯字的原理類似,保存多個不同數(shù)量的隨機(jī)像素的數(shù)組即可

<canvas id="drawing1" style="border:1px solid black"></canvas>
<button id="btn">開始顯字</button>
<script>
var drawing1 = document.getElementById('drawing1');
if(drawing1.getContext){
 var cxt = drawing1.getContext('2d');
 var W = drawing1.width = 200;
 var H = drawing1.height = 200;
 var str = '小火柴';
 cxt.textBaseline = 'top';
 var sh = 60;
 cxt.font = sh + 'px 宋體'
 var sw = cxt.measureText(str).width;
 if(sw > W){
  sw = W;
 }
 cxt.fillText(str,(W - sw)/2,(H - sh)/2,W);
 //獲取imageData
 var imageData = cxt.getImageData(0,0,W,H); 
 cxt.clearRect(0,0,W,H);
 //獲得10組粒子
 var imageDataArr = [];
 var n = 10;
 var index = 0;
 for(var i = n; i > 0; i--){
 imageDataArr.push(setData(imageData,1,i));
 }
 var oTimer = null;
 btn.onclick = function(){
 clearTimeout(oTimer);
 showData();
 }
 function showData(){
 oTimer = setTimeout(function(){
  cxt.clearRect(0,0,W,H);
  //寫入drawing1中 
  cxt.putImageData(imageDataArr[index++],0,0); 
  //迭代函數(shù)  
  showData();  
  if(index == 10){
  clearTimeout(oTimer);
  index = 0;
  }  
 },100);  
 } 
 function setData(imageData,n,m){
 //從imageData對象中取得粒子,并存儲到dots數(shù)組中
 var dots = [];
 for(var i = 0; i < W; i+=n){
  for(var j = 0; j < H ;j+=n){
  //data值中的紅色值
  var k = 4*(i + j*W);
  //data值中的透明度
  if(imageData.data[k+3] > 0){
   //將透明度大于0的data中的紅色值保存到dots數(shù)組中
   dots.push(k);
  }
  }
 } 
 //篩選粒子,僅保存dots.length/m個到newDots數(shù)組中
 var newDots = [];
 var len = Math.floor(dots.length/m);
 for(var i = 0; i < len; i++){
  newDots.push(Number(dots.splice(Math.floor(Math.random()*dots.length),1)));
 }
 //新建一個imageData,并將篩選后的粒子信息保存到新建的imageData中
 var oNewImage = cxt.createImageData(W,H);
 for(var i = 0; i < newDots.length; i++){
  oNewImage.data[newDots[i]+0] = imageData.data[newDots[i]+0];
  oNewImage.data[newDots[i]+1] = imageData.data[newDots[i]+1];
  oNewImage.data[newDots[i]+2] = imageData.data[newDots[i]+2];
  oNewImage.data[newDots[i]+3] = imageData.data[newDots[i]+3];
 }
 return oNewImage;
 }
}
</script>

粒子動畫

粒子動畫并不是粒子在做動畫,而是通過getImageData()方法獲得粒子的隨機(jī)坐標(biāo)和最終坐標(biāo)后,通過fillRect()方法繪制的小方塊在做運動。使用定時器,不斷的繪制坐標(biāo)變化的小方塊,以此來產(chǎn)生運動的效果

【隨機(jī)位置】

<canvas id="drawing1" style="border:1px solid black"></canvas>
<button id="btn1">開始顯字</button>
<button id="btn2">重新混亂</button>
<script>
var drawing1 = document.getElementById('drawing1');
if(drawing1.getContext){
 var cxt = drawing1.getContext('2d');
 var W = drawing1.width = 200;
 var H = drawing1.height = 200;
 var str = '小火柴';
 cxt.textBaseline = 'top';
 var sh = 60;
 cxt.font = sh + 'px 宋體'
 var sw = cxt.measureText(str).width;
 if(sw > W){
  sw = W;
 }
 cxt.fillText(str,(W - sw)/2,(H - sh)/2,W);
 //獲取imageData
 var imageData = cxt.getImageData(0,0,W,H); 
 cxt.clearRect(0,0,W,H);
 function setData(imageData,n,m){
 //從imageData對象中取得粒子,并存儲到dots數(shù)組中
 var dots = [];
 //dots的索引
 var index = 0;
 for(var i = 0; i < W; i+=n){
  for(var j = 0; j < H ;j+=n){
  //data值中的紅色值
  var k = 4*(i + j*W);
  //data值中的透明度
  if(imageData.data[k+3] > 0){
   //將透明度大于0的data中的紅色值保存到dots數(shù)組中
   dots.push(k);
   dots[index++] = {
   'index':index,
   'x':i,
   'y':j,
   'red':k,
   'randomX':Math.random()*W,
   'randomY':Math.random()*H,
   }
  }
  }
 } 
 //篩選粒子,僅保存dots.length/m個到newDots數(shù)組中
 var newDots = [];
 var len = Math.floor(dots.length/m);
 for(var i = 0; i < len; i++){
  newDots.push(dots.splice(Math.floor(Math.random()*dots.length),1)[0]);
 }
 return newDots;
 }
 //獲得粒子數(shù)組
 var dataArr = setData(imageData,1,1);
 var oTimer1 = null;
 var oTimer2 = null;
 btn1.onclick = function(){
 clearTimeout(oTimer1);
 showData(10);
 } 
 btn2.onclick = function(){
 clearTimeout(oTimer2);
 showRandom(10);
 } 
 function showData(n){
 oTimer1 = setTimeout(function(){
  cxt.clearRect(0,0,W,H);
  for(var i = 0; i < dataArr.length; i++){
  var temp = dataArr[i];
  var x0 = temp.randomX;
  var y0 = temp.randomY;
  var disX = temp.x - temp.randomX;
  var disY = temp.y - temp.randomY;
  cxt.fillRect(x0 + disX/n,y0 + disY/n,1,1); 
  } 
  showData(n-1); 
  if(n === 1){
  clearTimeout(oTimer1);
  }  
 },60); 
 } 
 function showRandom(n){
 oTimer2 = setTimeout(function fn(){
  cxt.clearRect(0,0,W,H);
  for(var i = 0; i < dataArr.length; i++){
  var temp = dataArr[i];
  var x0 = temp.x;
  var y0 = temp.y;
  var disX = temp.randomX - temp.x;
  var disY = temp.randomY - temp.y;
  cxt.fillRect(x0 + disX/n,y0 + disY/n,1,1);    
  }  
  showRandom(n-1); 
  if(n === 1){
  clearTimeout(oTimer2);
  }  
 },60); 
 } 
}
</script>

【飄入效果】 

飄入效果與隨機(jī)顯字的原理相似,不再贅述

<canvas id="drawing1" style="border:1px solid black"></canvas>
<button id="btn1">左上角飄入</button>
<script>
var drawing1 = document.getElementById('drawing1');
if(drawing1.getContext){
 var cxt = drawing1.getContext('2d');
 var W = drawing1.width = 200;
 var H = drawing1.height = 200;
 var str = '小火柴';
 cxt.textBaseline = 'top';
 var sh = 60;
 cxt.font = sh + 'px 宋體'
 var sw = cxt.measureText(str).width;
 if(sw > W){
  sw = W;
 }
 cxt.fillText(str,(W - sw)/2,(H - sh)/2,W);
 //獲取imageData
 var imageData = cxt.getImageData(0,0,W,H); 
 cxt.clearRect(0,0,W,H);
 function setData(imageData,n,m){
 //從imageData對象中取得粒子,并存儲到dots數(shù)組中
 var dots = [];
 //dots的索引
 var index = 0;
 for(var i = 0; i < W; i+=n){
  for(var j = 0; j < H ;j+=n){
  //data值中的紅色值
  var k = 4*(i + j*W);
  //data值中的透明度
  if(imageData.data[k+3] > 0){
   //將透明度大于0的data中的紅色值保存到dots數(shù)組中
   dots.push(k);
   dots[index++] = {
   'index':index,
   'x':i,
   'y':j,
   'red':k,
   'randomX':Math.random()*W,
   'randomY':Math.random()*H,
   }
  }
  }
 } 
 //篩選粒子,僅保存dots.length/m個到newDots數(shù)組中
 var newDots = [];
 var len = Math.floor(dots.length/m);
 for(var i = 0; i < len; i++){
  newDots.push(dots.splice(Math.floor(Math.random()*dots.length),1)[0]);
 }
 return newDots;
 }
 //獲得粒子數(shù)組
 var dataArr = setData(imageData,1,1);
 var oTimer1 = null;
 btn1.onclick = function(){
 clearTimeout(oTimer1);
 showData(10);
 } 
 function showData(n){
 oTimer1 = setTimeout(function(){
  cxt.clearRect(0,0,W,H);
  for(var i = 0; i < dataArr.length; i++){
  var temp = dataArr[i];
  var x0 = 0;
  var y0 = 0;
  var disX = temp.x - 0;
  var disY = temp.y - 0;
  cxt.fillRect(x0 + disX/n,y0 + disY/n,1,1); 
  } 
  showData(n-1); 
  if(n === 1){
  clearTimeout(oTimer1);
  }  
 },60); 
 } 
}
</script>

鼠標(biāo)交互

一般地,粒子的鼠標(biāo)交互都與isPointInPath(x,y)方法有關(guān)

【移入變色】

當(dāng)鼠標(biāo)接近粒子時,該粒子變紅。實現(xiàn)原理很簡單。鼠標(biāo)移動時,通過isPointInPath(x,y)方法檢測,有哪些粒子處于當(dāng)前指針范圍內(nèi)。如果處于,繪制1像素的紅色矩形即可

<canvas id="drawing1" style="border:1px solid black"></canvas>
<script>
var drawing1 = document.getElementById('drawing1');
if(drawing1.getContext){
 var cxt = drawing1.getContext('2d');
 var W = drawing1.width = 200;
 var H = drawing1.height = 200;
 var str = '小火柴';
 cxt.textBaseline = 'top';
 var sh = 60;
 cxt.font = sh + 'px 宋體'
 var sw = cxt.measureText(str).width;
 if(sw > W){
  sw = W;
 }
 cxt.fillText(str,(W - sw)/2,(H - sh)/2,W);
 //獲取imageData
 var imageData = cxt.getImageData(0,0,W,H); 
 function setData(imageData,n,m){
 //從imageData對象中取得粒子,并存儲到dots數(shù)組中
 var dots = [];
 //dots的索引
 var index = 0;
 for(var i = 0; i < W; i+=n){
  for(var j = 0; j < H ;j+=n){
  //data值中的紅色值
  var k = 4*(i + j*W);
  //data值中的透明度
  if(imageData.data[k+3] > 0){
   //將透明度大于0的data中的紅色值保存到dots數(shù)組中
   dots.push(k);
   dots[index++] = {
   'index':index,
   'x':i,
   'y':j,
   'red':k,
   'randomX':Math.random()*W,
   'randomY':Math.random()*H,
   }
  }
  }
 } 
 //篩選粒子,僅保存dots.length/m個到newDots數(shù)組中
 var newDots = [];
 var len = Math.floor(dots.length/m);
 for(var i = 0; i < len; i++){
  newDots.push(dots.splice(Math.floor(Math.random()*dots.length),1)[0]);
 }
 return newDots;
 }
 //獲得粒子數(shù)組
 var dataArr = setData(imageData,1,1); 
 //鼠標(biāo)移動時,當(dāng)粒子距離鼠標(biāo)指針小于10時,則進(jìn)行相關(guān)操作
 drawing1.onmousemove = function(e){
 e = e || event;
 var x = e.clientX - drawing1.getBoundingClientRect().left;
 var y = e.clientY - drawing1.getBoundingClientRect().top;
 cxt.beginPath();
 cxt.arc(x,y,10,0,Math.PI*2);
 for(var i = 0; i < dataArr.length; i++){
  var temp = dataArr[i];
  if(cxt.isPointInPath(temp.x,temp.y)){ 
  cxt.fillStyle = 'red';
  cxt.fillRect(temp.x,temp.y,1,1);
  }  
 } 
 }
}
</script>

【遠(yuǎn)離鼠標(biāo)】

鼠標(biāo)點擊時,以鼠標(biāo)指針為圓心的一定范圍內(nèi)的粒子需要移動到該范圍以外。一段時間后,粒子回到原始位置

實現(xiàn)原理并不復(fù)雜,使用isPointInPath(x,y)方法即可,如果粒子處于當(dāng)前路徑中,則沿著鼠標(biāo)指針與粒子坐標(biāo)組成的直線方向,移動到路徑的邊緣

<canvas id="drawing1" style="border:1px solid black"></canvas>
<script>
var drawing1 = document.getElementById('drawing1');
if(drawing1.getContext){
 var cxt = drawing1.getContext('2d');
 var W = drawing1.width = 200;
 var H = drawing1.height = 200;
 var str = '小火柴';
 cxt.textBaseline = 'top';
 var sh = 60;
 cxt.font = sh + 'px 宋體'
 var sw = cxt.measureText(str).width;
 if(sw > W){
  sw = W;
 }
 //渲染文字
 cxt.fillText(str,(W - sw)/2,(H - sh)/2,W);
 //獲取imageData
 var imageData = cxt.getImageData(0,0,W,H); 
 cxt.clearRect(0,0,W,H);
 function setData(imageData,n,m){
 //從imageData對象中取得粒子,并存儲到dots數(shù)組中
 var dots = [];
 //dots的索引
 var index = 0;
 for(var i = 0; i < W; i+=n){
  for(var j = 0; j < H ;j+=n){
  //data值中的紅色值
  var k = 4*(i + j*W);
  //data值中的透明度
  if(imageData.data[k+3] > 0){
   //將透明度大于0的data中的紅色值保存到dots數(shù)組中
   dots.push(k);
   dots[index++] = {
   'index':index,
   'x':i,
   'y':j,
   'red':k,
   'randomX':Math.random()*W,
   'randomY':Math.random()*H,
   'mark':false
   }
  }
  }
 } 
 //篩選粒子,僅保存dots.length/m個到newDots數(shù)組中
 var newDots = [];
 var len = Math.floor(dots.length/m);
 for(var i = 0; i < len; i++){
  newDots.push(dots.splice(Math.floor(Math.random()*dots.length),1)[0]);
 }
 return newDots;
 }
 //獲得粒子數(shù)組
 var dataArr = setData(imageData,2,1); 
 //將篩選后的粒子信息保存到新建的imageData中
 var oNewImage = cxt.createImageData(W,H);
 for(var i = 0; i < dataArr.length; i++){
 for(var j = 0; j < 4; j++){
  oNewImage.data[dataArr[i].red+j] = imageData.data[dataArr[i].red+j];
 }
 } 
 //寫入canvas中
 cxt.putImageData(oNewImage,0,0);
 //設(shè)置鼠標(biāo)檢測半徑為r
 var r = 20;
 //鼠標(biāo)移動時,當(dāng)粒子距離鼠標(biāo)指針小于20時,則進(jìn)行相關(guān)操作
 drawing1.onmousedown = function(e){
 e = e || event;
 var x = e.clientX - drawing1.getBoundingClientRect().left;
 var y = e.clientY - drawing1.getBoundingClientRect().top;
 cxt.beginPath();
 cxt.arc(x,y,r,0,Math.PI*2);
 for(var i = 0; i < dataArr.length; i++){
  var temp = dataArr[i];
  if(cxt.isPointInPath(temp.x,temp.y)){ 
  temp.mark = true;
  var angle = Math.atan2((temp.y - y),(temp.x - x));
  temp.endX = x - r*Math.cos(angle);
  temp.endY = y - r*Math.sin(angle);
  var disX = temp.x - temp.endX;
  var disY = temp.y - temp.endY;
  cxt.fillStyle = '#fff';
  cxt.fillRect(temp.x,temp.y,1,1);
  cxt.fillStyle = '#000';
  cxt.fillRect(temp.endX,temp.endY,1,1); 
  dataRecovery(10);
  }else{
  temp.mark = false;
  }  
 }
 var oTimer = null;
 function dataRecovery(n){
  clearTimeout(oTimer);
  oTimer = setTimeout(function(){
  cxt.clearRect(0,0,W,H);
  for(var i = 0; i < dataArr.length; i++){
   var temp = dataArr[i];
   if(temp.mark){
   var x0 = temp.endX;
   var y0 = temp.endY;
   var disX = temp.x - x0;
   var disY = temp.y - y0;
   cxt.fillRect(x0 + disX/n,y0 + disY/n,1,1); 
   }else{
   cxt.fillRect(temp.x,temp.y,1,1);
   }
  } 
  dataRecovery(n-1); 
  if(n === 1){
   clearTimeout(oTimer);
  }  
  },17);
 } 
 } 
}
</script>

綜合實例

下面將上面的效果制作為一個可編輯的綜合實例

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
</head>
<body>
<canvas id="drawing1" style="border:1px solid black"></canvas>
<br>
<div style="margin-bottom:10px">
 <span>粒子設(shè)置:</span>
 <input type="text" id="textValue" value="小火柴的藍(lán)色理想"> 
 <button id="btnSetText">文字設(shè)置確認(rèn)</button>
 <button id="btnchoose2">按序篩選</button>
 <button id="btnchoose3">隨機(jī)篩選</button>
 <button id="btnchoose1">不篩選</button> 
</div>
<div style="margin-bottom:10px">
 <span>粒子效果:</span>
 <button id="btn1">按序顯字</button>
 <button id="btn2">隨機(jī)顯字</button> 
 <button id="btn3">混亂聚合</button>
 <button id="btn4">重新混亂</button>
</div>
<div>
 <span>鼠標(biāo)效果:</span>
 <span>1、鼠標(biāo)移到文字上時,文字顏色變紅;</span>
 <span>2、鼠標(biāo)在文字上點擊時,粒子遠(yuǎn)離鼠標(biāo)指針</span>
</div>
<script>
if(drawing1.getContext){
 var cxt = drawing1.getContext('2d');
 var W = drawing1.width = 300;
 var H = drawing1.height = 200; 
 var imageData;
 var dataArr;
 btnSetText.onclick = function(){
 fnSetText(textValue.value);
 } 
 function fnSetText(str){
 cxt.clearRect(0,0,W,H);
 cxt.textBaseline = 'top';
 var sh = 60;
 cxt.font = sh + 'px 宋體'
 var sw = cxt.measureText(str).width;
 if(sw > W){
  sw = W;
 }
 cxt.fillText(str,(W - sw)/2,(H - sh)/2,W); 
 imageData = cxt.getImageData(0,0,W,H); 
 dataArr = setData(imageData,1,1); 
 }
 fnSetText('小火柴');
 btnchoose1.onclick = function(){
 dataArr = setData(imageData,1,1);
 saveData(dataArr); 
 }
 btnchoose2.onclick = function(){
 dataArr = setData(imageData,2,1);
 saveData(dataArr); 
 }
 btnchoose3.onclick = function(){
 dataArr = setData(imageData,1,2);
 saveData(dataArr); 
 } 
 //篩選粒子
 function setData(imageData,n,m){
 //從imageData對象中取得粒子,并存儲到dots數(shù)組中
 var dots = [];
 //dots的索引
 var index = 0;
 for(var i = 0; i < W; i+=n){
  for(var j = 0; j < H ;j+=n){
  //data值中的紅色值
  var k = 4*(i + j*W);
  //data值中的透明度
  if(imageData.data[k+3] > 0){
   //將透明度大于0的data中的紅色值保存到dots數(shù)組中
   dots.push(k);
   dots[index++] = {
   'index':index,
   'x':i,
   'y':j,
   'red':k,
   'green':k+1,
   'blue':k+2,
   'randomX':Math.random()*W,
   'randomY':Math.random()*H,
   'mark':false
   }
  }
  }
 } 
 //篩選粒子,僅保存dots.length/m個到newDots數(shù)組中
 var newDots = [];
 var len = Math.floor(dots.length/m);
 for(var i = 0; i < len; i++){
  newDots.push(dots.splice(Math.floor(Math.random()*dots.length),1)[0]);
 }
 return newDots;
 }
 function saveData(dataArr){
 //將篩選后的粒子信息保存到新建的imageData中
 var oNewImage = cxt.createImageData(W,H);
 for(var i = 0; i < dataArr.length; i++){
  for(var j = 0; j < 4; j++){
  oNewImage.data[dataArr[i].red+j] = imageData.data[dataArr[i].red+j];
  }
 }
 //寫入canvas中
 cxt.putImageData(oNewImage,0,0);  
 }
 //顯示粒子
 function showData(arr,oTimer,index,n){
 oTimer = setTimeout(function(){
  cxt.clearRect(0,0,W,H);
  //寫入canvas中 
  saveData(arr[index++]); 
  if(index == n){
  clearTimeout(oTimer);
  }else{
  //迭代函數(shù)  
  showData(arr,oTimer,index,n);   
  }      
 },60);  
 } 
 //重新混亂
 function showDataToRandom(dataArr,oTimer,n){
 oTimer = setTimeout(function fn(){
  cxt.clearRect(0,0,W,H);
  for(var i = 0; i < dataArr.length; i++){
  var temp = dataArr[i];
  var x0 = temp.x;
  var y0 = temp.y;
  var disX = temp.randomX - temp.x;
  var disY = temp.randomY - temp.y;
  cxt.fillRect(x0 + disX/n,y0 + disY/n,1,1);    
  } 
  n--;
  if(n === 0){
  clearTimeout(oTimer);
  }else{
  showDataToRandom(dataArr,oTimer,n); 
  }    
 },60); 
 } 
 //混亂聚合
 function showRandomToData(dataArr,oTimer,n){
 oTimer = setTimeout(function(){
  cxt.clearRect(0,0,W,H);
  for(var i = 0; i < dataArr.length; i++){
  var temp = dataArr[i];
  var x0 = temp.randomX;
  var y0 = temp.randomY;
  var disX = temp.x - temp.randomX;
  var disY = temp.y - temp.randomY;
  cxt.fillRect(x0 + disX/n,y0 + disY/n,1,1); 
  } 
  n--;
  if(n === 0){
  clearTimeout(oTimer);
  }else{
  showRandomToData(dataArr,oTimer,n); 
  }  
 },60); 
 }
 btn1.onclick = function(){
 btn1.arr = [];
 for(var i = 10; i > 1; i--){
  btn1.arr.push(setData(imageData,i,1));
 }
 showData(btn1.arr,btn1.oTimer,0,9);
 }
 btn2.onclick = function(){
 btn2.arr = [];
 for(var i = 10; i > 0; i--){
  btn2.arr.push(setData(imageData,2,i));
 }
 showData(btn2.arr,btn2.oTimer,0,10);
 } 
 btn3.onclick = function(){
 clearTimeout(btn3.oTimer);
 showRandomToData(dataArr,btn3.oTimer,10);
 }
 btn4.onclick = function(){
 clearTimeout(btn4.oTimer);
 showDataToRandom(dataArr,btn4.oTimer,10);
 } 
 //鼠標(biāo)移動
 drawing1.onmousemove = function(e){
 e = e || event;
 var x = e.clientX - drawing1.getBoundingClientRect().left;
 var y = e.clientY - drawing1.getBoundingClientRect().top;
 cxt.beginPath();
 cxt.arc(x,y,10,0,Math.PI*2);
 for(var i = 0; i < dataArr.length; i++){
  var temp = dataArr[i];
  if(cxt.isPointInPath(temp.x,temp.y)){ 
  cxt.fillStyle = 'red';
  cxt.fillRect(temp.x,temp.y,1,1);
  }  
 }
 cxt.fillStyle = 'black'; 
 } 
 //鼠標(biāo)點擊
 drawing1.onmousedown = function(e){
 var r = 20;
 e = e || event;
 var x = e.clientX - drawing1.getBoundingClientRect().left;
 var y = e.clientY - drawing1.getBoundingClientRect().top;
 cxt.beginPath();
 cxt.arc(x,y,r,0,Math.PI*2);
 for(var i = 0; i < dataArr.length; i++){
  var temp = dataArr[i];
  if(cxt.isPointInPath(temp.x,temp.y)){ 
  temp.mark = true;
  var angle = Math.atan2((temp.y - y),(temp.x - x));
  temp.endX = x - r*Math.cos(angle);
  temp.endY = y - r*Math.sin(angle);
  var disX = temp.x - temp.endX;
  var disY = temp.y - temp.endY;
  cxt.fillStyle = '#fff';
  cxt.fillRect(temp.x,temp.y,1,1);
  cxt.fillStyle = '#f00';
  cxt.fillRect(temp.endX,temp.endY,1,1); 
  cxt.fillStyle="#000";
  dataRecovery(10);
  }else{
  temp.mark = false;
  }  
 }
 var oTimer = null;
 function dataRecovery(n){
  clearTimeout(oTimer);
  oTimer = setTimeout(function(){
  cxt.clearRect(0,0,W,H);
  for(var i = 0; i < dataArr.length; i++){
   var temp = dataArr[i];
   if(temp.mark){
   var x0 = temp.endX;
   var y0 = temp.endY;
   var disX = temp.x - x0;
   var disY = temp.y - y0;
   cxt.fillRect(x0 + disX/n,y0 + disY/n,1,1); 
   }else{
   cxt.fillRect(temp.x,temp.y,1,1);
   }
  } 
  dataRecovery(n-1); 
  if(n === 1){
   clearTimeout(oTimer);
  }  
  },17);
 } 
 } 
}
</script> 
</body>
</html>

以上這篇基于canvas粒子系統(tǒng)的構(gòu)建詳解就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論