canvas 实现简单的粒子运动效果
实现效果如下:
设计思路
1.初始化画布
2.再自定义创建80个圆点(数量可自定义),然后初始化
3.然后实现是在一定距离范围内的圆点两两相连,并且运动起来
4.然后实现鼠标移进出现圆点与里面的圆点能相连
设计方法
1.初始化画布
// 初始化画布 let ele = document.getElementById('my_canvas'); ele.width = ele.offsetWidth; ele.height = ele.offsetHeight; let ctx = ele.getContext('2d');
2.创建圆与连线(使用构造函数,封装所有的方法与属性)
function random(min,max){ return min+Math.random()*(max-min); } // 创建圆(构造器函数) function circle(x,y){ this.x=x; this.y=y; this.radius=random(.8,4); // 偏移 this.speed_x=random(-1,1); this.speed_y=random(-1,1); // 移动 this.move=function (width,height){ this.speed_x=(this.x<width && this.x>0)?this.speed_x:(-this.speed_x); this.speed_y=(this.y<width && this.y>0)?this.speed_y:(-this.speed_y); this.x+=this.speed_x; this.y+=this.speed_y; } // 画圆 this.drawCircle=function (ctx){ ctx.beginPath(); ctx.fillStyle=`rgba(${random(0,255)},${random(0,255)},${random(0,255)},.6)` ctx.arc(this.x,this.y,this.radius,0,360); ctx.fill(); } // 画连线 this.drawLine=function(ctx,_circle){ let dx=this.x-_circle.x; let dy=this.y-_circle.y; let d=Math.sqrt( Math.pow(dx,2) + Math.pow(dy,2) ); // 勾股定理求距离 if(d<120){ ctx.beginPath(); ctx.moveTo(this.x,this.y); ctx.lineTo(_circle.x,_circle.y); ctx.strokeStyle='rgba(201, 201, 201, 0.5)'; ctx.stroke(); } } }
3. 创建80个点,将其初始化,放进数组存储,以便移动是可以利用(初始化之后,要调用draw方法绘制画面)
// 创建圆点集合数组 let circles=[]; let circleCount=80; // 初始化circleCount个圆 function init(){ for(let i=0;i<circleCount;i++){ circles.push(new circle(random(0,ele.width),random(0,ele.height))); } draw(); } init();
4.在画布上绘制出下面静态的画面
代码如下
// 通过调用draw()方法,实现每一次80个点的位置的变化(看似实在运动) function draw(){ // 清空上一次的画布 ctx.clearRect(0,0,ele.width,ele.height); for(let i=0;i<circles.length;i++){ circles[i].move(ele.width,ele.height); circles[i].drawCircle(ctx); for(let j=i+1;j<circles.length;j++){ circles[i].drawLine(ctx,circles[j]); } }
}
5.实现圆点的自由运动
上面的代码,是静态的画面(默认draw方法只执行一次),所以只能我们通过手动点击刷新来改变位置
若想自动更新,可以通过计时器主要是采用requestAnimationFrame来代替setInterval
下面的这段代码(适用于不同浏览器,解决了一些浏览器没有requestAnimationFrame这个方法)
1 window.requestAnimationFrame = window.requestAnimationFrame 2 || window.mozRequestAnimationFrame 3 || window.webkitRequestAnimationFrame 4 || window.msRequestAnimationFrame 5 || function(callback){ 6 setInterval(callback,16.7) 7 };
从上面的代码,我们可以引申出一个名词,叫做垫片(polyfill)->适配不同的浏览器缺少某个方法的一段算法 这段代码的作用就是解决一些浏览器没有requestAnimationFrame这个方法,
像这样的一段算法,或者说代码,是有名词来称呼它的叫做 垫片(polyfill)
该如何使用这个requestAnimationFrame呢??(注:红字为使用方法)
function draw(){ ctx.clearRect(0,0,ele.width,ele.height); for(let i=0;i<circles.length;i++){ circles[i].move(ele.width,ele.height); circles[i].drawCircle(ctx); for(let j=i+1;j<circles.length;j++){ circles[i].drawLine(ctx,circles[j]); } } requestAnimationFrame(draw); }
6. 实现上面例子中用鼠标移进的效果
构造出鼠标实时位置画圆点的函数
function currentCircle(x,y){ circle.call(this,x,y) this.drawCircle=function (ctx){ ctx.beginPath(); ctx.arc(this.x,this.y,this.radius,0,360); ctx.fillStyle='rgba(255,255,255,0)'; ctx.fill(); } }
注: 其中的circle.call(this,x,y)的代码call的第一个参数中是this,由于我们使用let currentPoint=new currentCircle(0,0)
所以this绑定在currentPoint这个对象身上,假设没有new这个关键字,那么1这个this指向的就是window(在非严格模式下)。
call 是能够改变this指向,表示了this的值从一个环境传到另一个环境。而将一个对象作为call的第一个参数,则this将绑定这个对象,也就是currentCircle;其他参数为传参。
因此,我们可以理解成把circle函数的所有方法和属性,用到的this都是 currentCircle的方法和属性。
例子
var a=7; var obj={ a:4 } function test(){ return this.a; } test() // 结果 7 test.call(obj) // 结果4
获取鼠标实时的位置
代码如下:
// 初始化鼠标的位置 (这个是对象) let currentPoint=new currentCircle(0,0); // 鼠标移进 window.onmousemove= function (event){ let e = event|| window.event; let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; let scrollY = document.documentElement.scrollTop || document.body.scrollTop; // 鼠标的坐标(实时改变) currentPoint.x = e.pageX || e.clientX + scrollX; currentPoint.y= e.pageY || e.clientY + scrollY; } // 鼠标移出 window.onmouseout = function() { currentPoint.x = null; currentPoint.y = null; }
draw绘制出鼠标的效果
function draw(){ // 清空上一次的画布 ctx.clearRect(0,0,ele.width,ele.height); for(let i=0;i<circles.length;i++){ circles[i].move(ele.width,ele.height); circles[i].drawCircle(ctx); for(let j=i+1;j<circles.length;j++){ circles[i].drawLine(ctx,circles[j]); } }
// 表示如果鼠标的位置不为null,就会执行下面的操作 if(currentPoint.x){ currentPoint.drawCircle(ctx); for(let k=0;k<circles.length;k++){ currentPoint.drawLine(ctx,circles[k]); } } requestAnimationFrame(draw); }
完整代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>canvas 粒子特效</title> 8 9 </head> 10 <body > 11 <style> 12 body { 13 margin: 0; 14 height: 100%; 15 } 16 17 canvas { 18 width: 100%; 19 height: 100%; 20 position: absolute; 21 top: 0px; 22 left: 0; 23 } 24 </style> 25 <canvas id="my_canvas" width="400" height="400"></canvas> 26 <script> 27 // 初始化画布 28 let ele = document.getElementById('my_canvas'); 29 ele.width = ele.offsetWidth; 30 ele.height = ele.offsetHeight; 31 let ctx = ele.getContext('2d'); 32 33 // 创建圆点集合数组 34 let circles=[]; 35 let circleCount=80; 36 let currentPoint=new currentCircle(0,0); 37 // 初始化circleCount个圆 38 function init(){ 39 for(let i=0;i<circleCount;i++){ 40 circles.push(new circle(random(0,ele.width),random(0,ele.height))); 41 } 42 draw(); 43 } 44 init(); 45 46 47 window.requestAnimationFrame = window.requestAnimationFrame 48 || window.mozRequestAnimationFrame 49 || window.webkitRequestAnimationFrame 50 || window.msRequestAnimationFrame 51 || function(callback){ 52 setInterval(callback,16.7) 53 }; 54 55 function random(min,max){ 56 return min+Math.random()*(max-min); 57 } 58 59 60 // 通过调用draw()方法,实现每一次80个点的位置的变化(看似实在运动) 61 function draw(){ 62 // 清空上一次的画布 63 ctx.clearRect(0,0,ele.width,ele.height); 64 65 for(let i=0;i<circles.length;i++){ 66 circles[i].move(ele.width,ele.height); 67 circles[i].drawCircle(ctx); 68 for(let j=i+1;j<circles.length;j++){ 69 circles[i].drawLine(ctx,circles[j]); 70 } 71 } 72 if(currentPoint.x){ 73 currentPoint.drawCircle(ctx); 74 for(let k=0;k<circles.length;k++){ 75 currentPoint.drawLine(ctx,circles[k]); 76 } 77 } 78 requestAnimationFrame(draw); 79 } 80 // 鼠标移进 81 window.onmousemove= function (event){ 82 let e = event|| window.event; 83 let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; 84 let scrollY = document.documentElement.scrollTop || document.body.scrollTop; 85 currentPoint.x = e.pageX || e.clientX + scrollX; 86 currentPoint.y= e.pageY || e.clientY + scrollY; 87 } 88 // 鼠标移出 89 window.onmouseout = function() { 90 currentPoint.x = null; 91 currentPoint.y = null; 92 } 93 94 95 function currentCircle(x,y){ 96 circle.call(this,x,y) 97 this.drawCircle=function (ctx){ 98 ctx.beginPath(); 99 ctx.arc(this.x,this.y,this.radius,0,360); 100 ctx.fillStyle='rgba(255,255,255,0)'; 101 ctx.fill(); 102 } 103 } 104 105 // 创建圆(构造器函数) 106 function circle(x,y){ 107 this.x=x; 108 this.y=y; 109 this.radius=random(.8,4); 110 // 偏移 111 this.speed_x=random(-1,1); 112 this.speed_y=random(-1,1); 113 114 // 移动 115 this.move=function (width,height){ 116 this.speed_x=(this.x<width && this.x>0)?this.speed_x:(-this.speed_x); 117 this.speed_y=(this.y<width && this.y>0)?this.speed_y:(-this.speed_y); 118 this.x+=this.speed_x; 119 this.y+=this.speed_y; 120 } 121 122 // 画圆 123 this.drawCircle=function (ctx){ 124 ctx.beginPath(); 125 ctx.fillStyle=`rgba(${random(0,255)},${random(0,255)},${random(0,255)},.6)` 126 ctx.arc(this.x,this.y,this.radius,0,360); 127 ctx.fill(); 128 } 129 130 // 画连线 131 this.drawLine=function(ctx,_circle){ 132 let dx=this.x-_circle.x; 133 let dy=this.y-_circle.y; 134 let d=Math.sqrt( Math.pow(dx,2) + Math.pow(dy,2) ); 135 // 勾股定理求距离 136 if(d<120){ 137 ctx.beginPath(); 138 ctx.moveTo(this.x,this.y); 139 ctx.lineTo(_circle.x,_circle.y); 140 ctx.strokeStyle='rgba(201, 201, 201, 0.5)'; 141 ctx.stroke(); 142 } 143 } 144 } 145 </script> 146 </body> 147 </html>