Web Front-end
JS Tutorial
Detailed explanation of the construction method of canvas particle system
Detailed explanation of the construction method of canvas particle system
The following editor will bring you a detailed explanation of the construction of canvas particle system. The editor thinks it’s pretty good, so I’ll share it with you now and give it as a reference. Let’s follow the editor and take a look.
What we said before
This article will start with the most basic theoretical knowledge of the imageData object. In detail Introducing the construction of the canvas particle system
imageData
There are three methods for image data, including getImageData(), putImageData(), createImageData ()
【getImageData()】
2D context can obtain the original image data through getImageData(). This method receives 4 parameters: the x and y coordinates of the screen area and the pixel width and height of the area
For example, to obtain the area with the coordinates of the upper left corner (10,5) and a size of 50*50 pixels For image data, you can use the following code:
var imageData = context.getImageData(10,5,50,50);
The returned object is an instance of ImageData. Each ImageData object has 3 properties: width\height\data
1. Width: Represents the width of the diagonal of imageData.
2. Height: Represents the height of the imageData object.
3. Data is an array that stores the data of each pixel in the image. . In the data array, each pixel is stored with 4 elements, representing red, green, blue, and transparency respectively.
[Note] How many pixels there are in the image, the length of data is equal to the number of pixels multiplied by 4
//第一个像素如下 var data = imageData.data; var red = data[0]; var green = data[1]; var blue = data[2]; var alpha = data[3];
The value of each element in the array is between 0-255. If you can directly access the original image data, you can operate these data in various ways
[Note] If the canvas to be obtained using getImageData() contains the drawImage() method, the URL in this method cannot cross domain
[createImageData()]
The createImageData(width,height) method creates a new blank ImageData object. The default pixel value of the new object is transparent black, which is equivalent to rgba(0,0,0,0)
var imgData = context.createImageData(100,100);
【putImageData()】
putImageData( ) method puts the image data back onto the canvas from the specified ImageData object. This method has the following parameters
imgData:要放回画布的ImageData对象(必须) x:imageData对象的左上角的x坐标(必须) y:imageData对象的左上角的y坐标(必须) dirtyX:在画布上放置图像的水平位置(可选) dirtyY:在画布上放置图像的垂直位置(可选) dirtyWidth:在画布上绘制图像所使用的宽度(可选) dirtyHeight:在画布上绘制图像所使用的高度(可选)
[Note] Parameters 3 to 7 are either none or all exist.
context.putImageData(imgData,0,0);
context.putImageData(imgData,0,0,50,50,200,200);
Particle writing
particles refer to each pixel in the image data imageData. The following is a simple example to illustrate complete writing and particle writing
[Full writing]
The text 'Little Match' exists in canvas1 of 200*200, and the entire canvas1 is used as an image Write data into canvas2 of the same size
<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>[Particle writing]
For complete writing, it is equivalent to a simple copy and paste. If you want fine control over each pixel, you need to use particle writing. There are a lot of blank areas in canvas1, and only the area with the words 'little match' is valid. Therefore, the particles can be filtered based on the transparency in the image data imageData, and only particles with a transparency greater than 0 are filtered out.
<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>Although the results look the same, canvas2 only uses 2336 out of 40000 particles in canvas1
Particle Filter
When the particles are fully written, the effect is the same as canvas copy and paste. When the particles are filtered, some wonderful effects will appear
【Sequential filtering】
Because when obtaining the particles, a double loop of width value * height value is used, and All increase by 1. If instead of adding 1, you add n, you can achieve the effect of sequential filtering
<canvas id="drawing1" style="border:1px solid black"></canvas>
<canvas id="drawing2" style="border:1px solid black"></canvas>
<p id="con">
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>
</p>
<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>[Random filtering]
In addition to using sequential filtering, Random screening can also be used. The position information of the particles obtained through the double loop is placed in the dots array. Filter through the splice() method, put the filtered position information into the newly created newDots array, and then use createImageData() to create a new image data object and return
<canvas id="drawing1" style="border:1px solid black"></canvas>
<canvas id="drawing2" style="border:1px solid black"></canvas>
<p id="con">
<button>1000</button>
<button>2000</button>
<button>3000</button>
<button>4000</button>
</p>
<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>Pixel display
Let’s use particle filtering to achieve the effect of a pixel display. Pixel display means gradually transitioning from unclear effect to full display
[Sequential pixel display]
The implementation principle of sequential pixel display is very simple. For example, there are 2000 particles in total. , a total of 10 levels of transition effects. Then use 10 arrays to store 200, 400, 600, 800, 100, 1200, 1400, 1600, 1800 and 2000 particles respectively. Then use the timer to display it gradually
<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>【Random pixel display】
The principle of random pixel display is similar, save multiple Arrays of different numbers of random pixels can
<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>Particle Animation
粒子动画并不是粒子在做动画,而是通过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>
<p 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>
</p>
<p style="margin-bottom:10px">
<span>粒子效果:</span>
<button id="btn1">按序显字</button>
<button id="btn2">随机显字</button>
<button id="btn3">混乱聚合</button>
<button id="btn4">重新混乱</button>
</p>
<p>
<span>鼠标效果:</span>
<span>1、鼠标移到文字上时,文字颜色变红;</span>
<span>2、鼠标在文字上点击时,粒子远离鼠标指针</span>
</p>
<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>The above is the detailed content of Detailed explanation of the construction method of canvas particle system. For more information, please follow other related articles on the PHP Chinese website!
Hot AI Tools
Undresser.AI Undress
AI-powered app for creating realistic nude photos
AI Clothes Remover
Online AI tool for removing clothes from photos.
Undress AI Tool
Undress images for free
Clothoff.io
AI clothes remover
AI Hentai Generator
Generate AI Hentai for free.
Hot Article
Hot Tools
Notepad++7.3.1
Easy-to-use and free code editor
SublimeText3 Chinese version
Chinese version, very easy to use
Zend Studio 13.0.1
Powerful PHP integrated development environment
Dreamweaver CS6
Visual web development tools
SublimeText3 Mac version
God-level code editing software (SublimeText3)
Hot Topics
1386
52
How to delete WeChat friends? How to delete WeChat friends
Mar 04, 2024 am 11:10 AM
WeChat is one of the mainstream chat tools. We can meet new friends, contact old friends and maintain the friendship between friends through WeChat. Just as there is no such thing as a banquet that never ends, disagreements will inevitably occur when people get along with each other. When a person extremely affects your mood, or you find that your views are inconsistent when you get along, and you can no longer communicate, then we may need to delete WeChat friends. How to delete WeChat friends? The first step to delete WeChat friends: tap [Address Book] on the main WeChat interface; the second step: click on the friend you want to delete and enter [Details]; the third step: click [...] in the upper right corner; Step 4: Click [Delete] below; Step 5: After understanding the page prompts, click [Delete Contact]; Warm
How to write a novel in the Tomato Free Novel app. Share the tutorial on how to write a novel in Tomato Novel.
Mar 28, 2024 pm 12:50 PM
Tomato Novel is a very popular novel reading software. We often have new novels and comics to read in Tomato Novel. Every novel and comic is very interesting. Many friends also want to write novels. Earn pocket money and edit the content of the novel you want to write into text. So how do we write the novel in it? My friends don’t know, so let’s go to this site together. Let’s take some time to look at an introduction to how to write a novel. Share the Tomato novel tutorial on how to write a novel. 1. First open the Tomato free novel app on your mobile phone and click on Personal Center - Writer Center. 2. Jump to the Tomato Writer Assistant page - click on Create a new book at the end of the novel.
How to enter bios on Colorful motherboard? Teach you two methods
Mar 13, 2024 pm 06:01 PM
Colorful motherboards enjoy high popularity and market share in the Chinese domestic market, but some users of Colorful motherboards still don’t know how to enter the bios for settings? In response to this situation, the editor has specially brought you two methods to enter the colorful motherboard bios. Come and try it! Method 1: Use the U disk startup shortcut key to directly enter the U disk installation system. The shortcut key for the Colorful motherboard to start the U disk with one click is ESC or F11. First, use Black Shark Installation Master to create a Black Shark U disk boot disk, and then turn on the computer. When you see the startup screen, continuously press the ESC or F11 key on the keyboard to enter a window for sequential selection of startup items. Move the cursor to the place where "USB" is displayed, and then
How to recover deleted contacts on WeChat (simple tutorial tells you how to recover deleted contacts)
May 01, 2024 pm 12:01 PM
Unfortunately, people often delete certain contacts accidentally for some reasons. WeChat is a widely used social software. To help users solve this problem, this article will introduce how to retrieve deleted contacts in a simple way. 1. Understand the WeChat contact deletion mechanism. This provides us with the possibility to retrieve deleted contacts. The contact deletion mechanism in WeChat removes them from the address book, but does not delete them completely. 2. Use WeChat’s built-in “Contact Book Recovery” function. WeChat provides “Contact Book Recovery” to save time and energy. Users can quickly retrieve previously deleted contacts through this function. 3. Enter the WeChat settings page and click the lower right corner, open the WeChat application "Me" and click the settings icon in the upper right corner to enter the settings page.
How to set font size on mobile phone (easily adjust font size on mobile phone)
May 07, 2024 pm 03:34 PM
Setting font size has become an important personalization requirement as mobile phones become an important tool in people's daily lives. In order to meet the needs of different users, this article will introduce how to improve the mobile phone use experience and adjust the font size of the mobile phone through simple operations. Why do you need to adjust the font size of your mobile phone - Adjusting the font size can make the text clearer and easier to read - Suitable for the reading needs of users of different ages - Convenient for users with poor vision to use the font size setting function of the mobile phone system - How to enter the system settings interface - In Find and enter the "Display" option in the settings interface - find the "Font Size" option and adjust it. Adjust the font size with a third-party application - download and install an application that supports font size adjustment - open the application and enter the relevant settings interface - according to the individual
Summary of methods to obtain administrator rights in Win11
Mar 09, 2024 am 08:45 AM
A summary of how to obtain Win11 administrator rights. In the Windows 11 operating system, administrator rights are one of the very important permissions that allow users to perform various operations on the system. Sometimes, we may need to obtain administrator rights to complete some operations, such as installing software, modifying system settings, etc. The following summarizes some methods for obtaining Win11 administrator rights, I hope it can help you. 1. Use shortcut keys. In Windows 11 system, you can quickly open the command prompt through shortcut keys.
The secret of hatching mobile dragon eggs is revealed (step by step to teach you how to successfully hatch mobile dragon eggs)
May 04, 2024 pm 06:01 PM
Mobile games have become an integral part of people's lives with the development of technology. It has attracted the attention of many players with its cute dragon egg image and interesting hatching process, and one of the games that has attracted much attention is the mobile version of Dragon Egg. To help players better cultivate and grow their own dragons in the game, this article will introduce to you how to hatch dragon eggs in the mobile version. 1. Choose the appropriate type of dragon egg. Players need to carefully choose the type of dragon egg that they like and suit themselves, based on the different types of dragon egg attributes and abilities provided in the game. 2. Upgrade the level of the incubation machine. Players need to improve the level of the incubation machine by completing tasks and collecting props. The level of the incubation machine determines the hatching speed and hatching success rate. 3. Collect the resources required for hatching. Players need to be in the game
Detailed explanation of Oracle version query method
Mar 07, 2024 pm 09:21 PM
Detailed explanation of Oracle version query method Oracle is one of the most popular relational database management systems in the world. It provides rich functions and powerful performance and is widely used in enterprises. In the process of database management and development, it is very important to understand the version of the Oracle database. This article will introduce in detail how to query the version information of the Oracle database and give specific code examples. Query the database version of the SQL statement in the Oracle database by executing a simple SQL statement


