亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

基于canvas粒子系統的構建詳解

2019-11-19 15:35:55
字體:
來源:轉載
供稿:網友

前面的話

本文將從最基本的imageData對象的理論知識說開去,詳細介紹canvas粒子系統的構建

imageData

關于圖像數據imageData共有3個方法,包括getImageData()、putImageData()、createImageData()

【getImageData()】

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

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

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

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

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

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

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

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

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

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

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

【createImageData()】

createImageData(width,height)方法創建新的空白ImageData對象。新對象的默認像素值 transparent black,相當于rgba(0,0,0,0)

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

【putImageData()】

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

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

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

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

粒子寫入

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

【完全寫入】

200*200的canvas1中存在文字'小火柴',并將canvas1整個作為圖像數據寫入同樣尺寸的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>

【粒子寫入】

對于完全寫入而言,相當于只是簡單的復制粘貼,如果要對每個像素點進行精細地控制,則需要使用粒子寫入。canvas1中存在著大量的空白區域,只有'小火柴'這三個字的區域是有效的。于是,可以根據圖像數據imageData中的透明度對粒子進行篩選,只篩選出透明度大于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數組中 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數組中   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>

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

粒子篩選

當粒子完全寫入時,與canvas復制粘貼的效果相同。而當粒子有所篩選時,則會出現一些奇妙的效果

【按序篩選】

由于取得粒子時,使用的是寬度值*高度值的雙重循環,且都以加1的形式遞增。如果不是加1,而是加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數組中 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數組中   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>

【隨機篩選】

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

<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數組中 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數組中   dots.push(k);  }  } }  //篩選粒子,僅保存m個到newDots數組中。如果不傳入m,則不進行篩選 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>

像素顯字

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

【按序像素顯字】

按序像素顯字的實現原理非常簡單,比如,共有2000個粒子,共10個程度的過渡效果。則使用10個數組,分別保存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);   //迭代函數    showData();    if(index == 10){    index = 0;  clearTimeout(oTimer);  }   },100);   }  function setData(imageData,n,m){ //從imageData對象中取得粒子,并存儲到dots數組中 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數組中   dots.push(k);  }  } }  //篩選粒子,僅保存m個到newDots數組中。如果不傳入m,則不進行篩選 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>

【隨機像素顯字】

隨機像素顯字的原理類似,保存多個不同數量的隨機像素的數組即可

<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);   //迭代函數    showData();    if(index == 10){  clearTimeout(oTimer);  index = 0;  }   },100);   }  function setData(imageData,n,m){ //從imageData對象中取得粒子,并存儲到dots數組中 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數組中   dots.push(k);  }  } }  //篩選粒子,僅保存dots.length/m個到newDots數組中 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()方法獲得粒子的隨機坐標和最終坐標后,通過fillRect()方法繪制的小方塊在做運動。使用定時器,不斷的繪制坐標變化的小方塊,以此來產生運動的效果

【隨機位置】

<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數組中 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數組中   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數組中 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; } //獲得粒子數組 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>

【飄入效果】 

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

<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數組中 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數組中   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數組中 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; } //獲得粒子數組 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>

鼠標交互

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

【移入變色】

當鼠標接近粒子時,該粒子變紅。實現原理很簡單。鼠標移動時,通過isPointInPath(x,y)方法檢測,有哪些粒子處于當前指針范圍內。如果處于,繪制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數組中 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數組中   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數組中 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; } //獲得粒子數組 var dataArr = setData(imageData,1,1);  //鼠標移動時,當粒子距離鼠標指針小于10時,則進行相關操作 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>

【遠離鼠標】

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

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

<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數組中 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數組中   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數組中 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; } //獲得粒子數組 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); //設置鼠標檢測半徑為r var r = 20; //鼠標移動時,當粒子距離鼠標指針小于20時,則進行相關操作 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>粒子設置:</span> <input type="text" id="textValue" value="小火柴的藍色理想">  <button id="btnSetText">文字設置確認</button> <button id="btnchoose2">按序篩選</button> <button id="btnchoose3">隨機篩選</button> <button id="btnchoose1">不篩選</button> </div><div style="margin-bottom:10px"> <span>粒子效果:</span> <button id="btn1">按序顯字</button> <button id="btn2">隨機顯字</button>  <button id="btn3">混亂聚合</button> <button id="btn4">重新混亂</button></div><div> <span>鼠標效果:</span> <span>1、鼠標移到文字上時,文字顏色變紅;</span> <span>2、鼠標在文字上點擊時,粒子遠離鼠標指針</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數組中 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數組中   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數組中 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{  //迭代函數    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); }  //鼠標移動 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';  }  //鼠標點擊 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粒子系統的構建詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日韩国产黄| 成人av资源在线播放| 欧美极品xxxx| 久久视频在线免费观看| 97久久精品国产| 91九色视频导航| 91久久综合亚洲鲁鲁五月天| 欧美大荫蒂xxx| 国产不卡视频在线| 亚洲男人的天堂网站| 91九色在线视频| 日韩在线免费视频观看| 欧美不卡视频一区发布| 成人黄色片在线| 欧美巨乳在线观看| 91中文字幕一区| 亚洲免费高清视频| 一区二区三区无码高清视频| 3344国产精品免费看| 亚洲第一免费播放区| 97久久精品国产| 精品亚洲国产视频| 国产视频久久久久| 国产欧美精品一区二区| 在线电影欧美日韩一区二区私密| 国产日韩欧美电影在线观看| 欧美成年人网站| 亚洲国产成人一区| 色老头一区二区三区在线观看| 2021久久精品国产99国产精品| 亚洲天堂成人在线视频| 欧美理论在线观看| 欧美人交a欧美精品| 国产欧美日韩精品专区| 欧美性生活大片免费观看网址| 色悠久久久久综合先锋影音下载| 亚洲人成电影网站色xx| 在线观看91久久久久久| 91精品国产91久久| 国产精品久久久久久中文字| 91视频国产精品| 亚洲偷熟乱区亚洲香蕉av| 亚洲www永久成人夜色| 欧美亚洲午夜视频在线观看| 精品久久久久久久久国产字幕| 4k岛国日韩精品**专区| 亚洲视频在线观看网站| 55夜色66夜色国产精品视频| 久久91亚洲精品中文字幕奶水| 综合网日日天干夜夜久久| 久青草国产97香蕉在线视频| 久久亚洲一区二区三区四区五区高| 亚洲天堂一区二区三区| 国产性猛交xxxx免费看久久| 69影院欧美专区视频| 上原亚衣av一区二区三区| 色噜噜狠狠狠综合曰曰曰| 国产日韩精品一区二区| 亚洲成人精品在线| 欧美成人四级hd版| 国产成人精品久久| 日韩电影在线观看永久视频免费网站| 97精品视频在线播放| 亚洲精品理论电影| 亚洲人成网站色ww在线| 亚洲激情在线观看| 国产亚洲精品美女| 国产精品女视频| 欧美wwwxxxx| 国产激情视频一区| 精品久久久精品| 日韩av在线看| 91香蕉嫩草神马影院在线观看| 欧美精品激情视频| 国产91精品在线播放| 亚洲精品suv精品一区二区| 国产一区视频在线播放| 亚洲欧美激情另类校园| 亚洲成人精品久久久| 在线看片第一页欧美| 色综合天天狠天天透天天伊人| 成人免费高清完整版在线观看| 亚洲欧美国产精品久久久久久久| 91久久精品美女高潮| 欧美日韩xxx| 欧美激情免费看| 中文字幕欧美日韩va免费视频| 欧美精品激情在线观看| 成人a在线观看| 欧美人与物videos| 欧美精品制服第一页| 欧美激情国产日韩精品一区18| 欧美香蕉大胸在线视频观看| 亲子乱一区二区三区电影| 成人在线观看视频网站| 欧洲永久精品大片ww免费漫画| 97在线观看免费| 91精品视频大全| 亚洲影院污污.| 久久精品亚洲热| 日韩精品免费在线视频| 国产精品久久久久久久久免费| 国产精品国产福利国产秒拍| 日韩**中文字幕毛片| 91视频8mav| 久久久久免费精品国产| 国产日韩欧美夫妻视频在线观看| 中文字幕日韩在线观看| 日韩在线一区二区三区免费视频| 91视频国产高清| 欧美亚洲第一区| 不卡中文字幕av| 欧美交受高潮1| 亚洲成色777777在线观看影院| 亚洲伊人久久综合| 一区二区三区在线播放欧美| 国产精品久久久久久中文字| 久久久av免费| 欧美激情videoshd| 欧美电影免费观看| 亚洲国产一区二区三区四区| 国产成人精品视频在线| 日韩av电影在线免费播放| 91精品国产综合久久香蕉922| 91亚洲国产成人久久精品网站| 深夜精品寂寞黄网站在线观看| 国内精品国产三级国产在线专| 精品毛片三在线观看| 国产欧美va欧美va香蕉在线| 亚洲日韩中文字幕| 亚洲成**性毛茸茸| 精品国产欧美成人夜夜嗨| 91国产一区在线| 亚洲成年网站在线观看| 亚洲欧美国产另类| 国产自摸综合网| 亚洲欧美中文在线视频| 日产精品99久久久久久| 国产一区二区三区在线观看网站| 久久影视电视剧免费网站清宫辞电视| 亚洲天堂av在线免费观看| 91香蕉嫩草影院入口| 国产精品久久久久久五月尺| 亚洲欧美三级在线| 国产精品丝袜白浆摸在线| 国内精品视频久久| 日韩久久免费视频| 久热精品视频在线观看一区| 国产精品白丝jk喷水视频一区| 国产精品久久久久久婷婷天堂| 日韩在线观看免费高清| 精品国产91久久久久久老师| 国产午夜精品全部视频在线播放| 亚洲福利影片在线| 精品无码久久久久久国产| 久久九九国产精品怡红院| 欧美另类第一页| 国产不卡视频在线| 亚洲精品欧美日韩| 国产精品久久久久久久av电影| 国产成人精品综合久久久| 久久久午夜视频| 91人人爽人人爽人人精88v| 久久av在线看|