该效果是在Canvas画布上制作的必威:,  关于

1. 开立二个<canvas>成分,并收获Canvas画布渲染上下文

必威 1

< canvas>是二个双标签成分,通过width和height的值来设置画布的尺寸。至于ctx(画布渲染上下文卡塔 尔(英语:State of Qatar),可知为画布上的画笔,大家得以因此画笔在画布上自便的绘图图案。若是浏览器不支持canvas会向来呈现<canvas>标签中间自个儿设定的文字。当然<canvas>标签中间也能够是一张当不援助canvas时需求替换显示的图形。

 

2. 使用canvas的图像操作API绘制图像

绘图图像的重大API及参数表明:

必威 2

援引MDN上的一张图会相比较清楚的看看每一种参数的魔法:

必威 3

drawImage正是把叁个image对象可能canvas上(以致是video对象上的的每豆蔻梢头帧卡塔尔钦赐地点和尺寸的图像绘制到方今的画布上。而在我们的须要中,是要把全路图像绘制到画布中。

必威 4

对应浏览器见到的效果:

必威 5

 

依据canvas粒子系统的构建详细解释,canvas粒子营造

前边的话

正文将从最基本的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个属性:widthheightdata

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()方法,则该措施中的UPRADOL不可能跨域

【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的canvas第11中学存在文字'大火柴',并将canvas1全体作为图像数据写入雷同尺寸的canvas第22中学

<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>

【粒子写入】

对此截然写入来讲,相当于只是轻巧的复制粘贴,如若要对各种像素点举办精密地决定,则必要运用粒子写入。canvas第11中学留存着一大波的空白区域,唯有'文火柴'那几个字的区域是实用的。于是,可以依据图像数据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只利用了canvas第11中学40000个粒子中的23四十八个

粒子筛选

当粒子完全写入时,与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>

像素显字

上面来利用粒子筛选来完结贰个像素显字的法力。像素显字即未有清晰的法力慢慢过渡到完全展现

【按序像素显字】

按序像素显字的落实原理极度轻巧,比如,共有2002个粒子,共12个档案的次序的过渡效果。则接纳十一个数组,分别保存200,400,600,800,100,1200,1400,1600,1800和二零零三个粒子。然后利用沙漏将其稳步显示出来即可

<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">
 粒子设置:
 <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">
 粒子效果:
 <button id="btn1">按序显字</button>
 <button id="btn2">随机显字</button> 
 <button id="btn3">混乱聚合</button>
 <button id="btn4">重新混乱</button>
</div>
<div>
 鼠标效果:
 1、鼠标移到文字上时,文字颜色变红;
 2、鼠标在文字上点击时,粒子远离鼠标指针
</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粒子系统的营造详细明白便是我分享给大家的全体内容了,希望能给大家二个参阅,也可望大家多多指教帮客之家。

前边的话 本文将从最基本的imageData对象的理论知识说开去,详细介绍canvas粒子系统的创设...

像素显字

  上面来利用粒子筛选来贯彻贰个像素显字的效应。像素显字即未有清晰的效应日趋对接到完全展现

【按序像素显字】

  按序像素显字的落到实处原理极度轻巧,举个例子,共有二〇〇四个粒子,共13个水平的接入效果。则动用12个数组,分别保存200,400,600,800,100,1200,1400,1600,1800和二零零二个粒子。然后利用电磁打点计时器将其稳步显示出来就可以

<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> 

 

  第叁个像素音讯为:兰德TucsonGBA(data[0],data[1],data[2],data[3])

构建缓动作效果果有三种格局:

风度翩翩种是和煦设定好调节点,然后经过贝塞尔曲线公式来总计每种单位时间的坐标值。

引用了wikipedia其间的图:

必威 6必威 7

上边三个图都以在绘制一条特定曲线,能够见到叁遍曲线须求二个特定调控点P1,一次曲线要求多少个特定调控点P1和P2来显明一条曲线,高阶曲线以致供给更加多的调控点来鲜明曲线轨迹。

求曲线的公式是依靠德卡斯特Rio算法测算得来的,直接上公式。

二回曲线对应的公式:

必威 8

一回曲线对应的公式:

必威 9

从公式能够见见,只要鲜明调控点坐标、开首坐标和尖峰坐标后,就足以分明了一条曲线,然后就足以依据曲线公式求出种种时刻t对应的岗位值B(t)。

本来使用这种措施必要和睦去制订调整点坐标,总计也相比较复杂,实现起来很麻烦。没事,大家还会有别的办法明确曲线。

 

别的黄金年代种办法正是选择已某些缓动函数,无需团结制订调整点,这里推荐有名的Tween算法的缓动函数,用此中二个缓动函数来介绍下参数值,其余缓动函数所传的参数值是同等的:

必威 10

是或不是以为很熟练?对科学,jquery用的卡通扩展插件easing.js就是Tween算法的缓动函数。有了那现成的缓动函数,就能够拟定粒子的伊始点、终点(终点便是美术自己的坐标地方卡塔尔国甚至动漫试行持续时间来做大家要的效劳。

根本参照他事他说加以侦查代码:

必威 11

依据参谋代码做出一个效果:

必威 12

啊,动漫效果是有了,但总感觉不太对劲儿。。。唔,留神观看一下,是丹青动漫执行太过完整了,未有明白的颗粒动漫效果,这就引出粒子动漫的另三个关键点,粒子试行动漫的机缘。

 

粒子写入

  粒子,指图像数据imageData中的每一个像素点。下边以二个简易实例来证实完全写入与粒子写入

【完全写入】

  200*200的canvas第11中学留存文字'温火柴',并将canvas1整个作为图像数据写入同样尺寸的canvas第22中学

<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>  

【粒子写入】

  对于截然写入来说,也正是只是简短的复制粘贴,假使要对各种像素点进行精细地调节,则必要利用粒子写入。canvas第11中学设有着多量的空白区域,唯有'大火柴'那多个字的区域是平价的。于是,能够依据图像数据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只行使了canvas第11中学40000个粒子中的23三十多少个

 

  

意气风发、绘制粒子轮廓图

先是要在canvas画布上绘制一个由粒子构成的概况图,记录下每贰个粒子的坐标,那样本领有持续的卡通。

总结实例

  上面将地点的成效制作为叁个可编写制定的综合实例

<!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">
  粒子设置:
  <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">
  粒子效果:
  <button id="btn1">按序显字</button>
  <button id="btn2">随机显字</button>  
  <button id="btn3">混乱聚合</button>
  <button id="btn4">重新混乱</button>
</div>
<div>
  鼠标效果:
  1、鼠标移到文字上时,文字颜色变红;
  2、鼠标在文字上点击时,粒子远离鼠标指针
</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>

 

重返值表明:imageData为重临值,它是三个对象,满含多个天性

创设高大上的Canvas粒子动漫

2016/08/22 · HTML5 · 5 评论 · Canvas

初藳出处: 腾讯ISUX   

率先来看下我们筹算要做的粒子动漫效果是如何的~

是这样:

必威 13

或许是那样:

必威 14

以致是如此:

必威 15

很酷炫!

那什么去得以达成相似下面的粒子动漫以至依据本身的喜好去做越来越多其余轨迹的卡通片呢~请看上边详细的上书。

意义务演出示

  下边是实例效果演示,博文结尾有百分百源码

 

imageData={
    data:Unit8ClampedArray[10000] //一个包含图片区域内每个像素点的RGBA的整型数据信息
    height:200   //读取的图片像素信息区域高度
    width:200   //读取的图片像素信息区域宽度
}

本领选用

因为粒子数量众多,何况波及到图像像素管理,所以这里运用Canvas是不二选取。

 

留意,以下演示的代码只是关键代码,入眼在于化解思路。

鼠标交互作用

  日常地,粒子的鼠标交互作用都与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>  

  使用鼠标点击canvas中的文字,现身效果

 

var imageData=ctx.getImageData(x, y, width, height);

3. 收获图像的像素音讯,并基于像素消息重新绘制出粒子效果轮廓图

canvas有二个叫getImageData的接口,通过该接口能够博获得画布上钦定地方的方方面面像素的多寡:

必威 16

把收获的imageData输出到调整台能够见到,imageData包蕴八个脾性:

必威 17

此中,width、height是读取图像像素音信完整区域的宽度和可观,data是叁个Uint8ClampedArray类型的风流倜傥维数组,满含了全部图片区域里各个像素点的CR-VGBA的整型数据。这里不可不要驾驭那几个数组所保存像素消息的排序法则,请看下图描述的data数组:

必威 18

每叁个色值攻下data数组索引的叁个职位,七个像素有个4个值(奥迪Q5、G、B、A卡塔尔攻陷数组的4个目录地方。遵照数列准绳能够理解,要赢得第n个职分(n从1最初卡塔尔国的Tiguan、G、B像素音信正是:奇骏n = (n-1)*4 ,Gn = (n-1)*4+1 ,Bn = (n-1)*4+2  ,so easy~  当然,实际上海教室疑似叁个囊括image.height行,image.width列像素的矩形并非单独的意气风发行到甘休的,这些n值在矩形中要计算下:

必威 19

是因为八个像素是包含4个索引值(rgba卡塔尔国的,所以得到图像中第i行第j列的昂Cora、G、B、A像素新闻正是Rij = [(j-1)*imageData.width + (i-1)]*4 ,Gij = [(j-1)*imageData.width + (i-1)]*4 + 1,Bij = [(j-1)*imageData.width + (i-1)]*4 + 2,Aij = [(j-1)*imageData.width + (i-1)]*4 + 3 。各类像素值都得以拿到了!

接下去就要把图像的粒子化轮廓图画出来了。那么,怎么做那个概况图啊,咱们先读取每一种像素的音讯(用到上边的总结公式),借使这么些像素的色值切合必要,就保存起来,用于绘制在画布上。其余,既然是做成粒子的机能,大家只须求把像素粒子保存生龙活虎部分,体现在画布上。

具体做法是,设定每意气风发行和每一列要出示的粒子数,分别是cols和rows,多个粒子代表叁个单元格,那么各种单元格的的宽高便是imageWidth/cols和imageHeight/rows,然后循环的判别每种单元格的率先个像素是还是不是满足像素值的准则,假诺满足了,就把那几个单元格的坐标保存到数组里,用作后续绘制图案用。

重在参照他事他说加以考查代码:

必威 20

用后生可畏体化代码做出的demo及效果:

必威 21

于今,粒子概略图曾经制作完了。

 

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个属性:widthheightdata

  1、width:表示imageData对角的宽度

  2、height:表示imageData对象的惊人

  3、data是八个数组,保存着图像中每三个像素的数量。在data数组中,每一个像素用4个成分来保存,分别表示red、green、blue、反射率

  [注意]图像中有稍许像素,data的长度就等于像素个数乘以4

必威 22

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

必威 23

  数组中各种成分的值是在0-255中间,能够从来访谈到原本图像数据,即能够种种法子来操作那一个多少

  [注意]假使要采纳getImageData()获取的canvas中包蕴drawImage()方法,则该方法中的USportageL不能够跨域

【createImageData()】

  createImageData(width,height)方法创造新的空白ImageData对象。新目的的暗中认可像素值 transparent black,也便是rgba(0,0,0,0)

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

【putImageData()】

  putImageData()方法将图像数据从钦赐的ImageData对象放回画布上,该格局共有以下参数

必威 24

imgData:要放回画布的ImageData对象(必须)
x:imageData对象的左上角的x坐标(必须)
y:imageData对象的左上角的y坐标(必须)
dirtyX:在画布上放置图像的水平位置(可选)
dirtyY:在画布上放置图像的垂直位置(可选)
dirtyWidth:在画布上绘制图像所使用的宽度(可选)
dirtyHeight:在画布上绘制图像所使用的高度(可选)

必威 25

  [注意]参数3到7要么都尚未,要么都存在

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

 

2:掌握像素区域数据的排布表达,以上得到的图形数据像素音讯(imageData对象中的data属性)为凯雷德GBA整型的意气风发维数组数据。一个像素是有4个值(悍马H2,G,B,A卡塔尔组成的。也正是说,数组音讯每八个为一个像素点。由此,有以下准绳,

粒子动漫

  粒子动漫并非粒子在做动漫,而是通过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>  

 

      第N个像素新闻为: EnclaveGBA(data[(n-1)*4],data[(n-1)*4+1],data[(n-1)*4+2],data[(n-1)*4+3])

本文由必威发布于必威-前端,转载请注明出处:该效果是在Canvas画布上制作的必威:,  关于

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。