canvas 实现简单的粒子运动效果

canvas 实现简单的粒子运动效果

实现效果如下

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.在画布上绘制出下面静态的画面

canvas 实现简单的粒子运动效果

代码如下

 // 通过调用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); }

完整代码如下:

  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              100%;
 19             height: 100%;
 20             position: absolute;
 21             top: 0px;
 22             left: 0;
 23         }
 24     </style>
 25     <canvas ></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>
View Code