zoukankan      html  css  js  c++  java
  • canvas粒子线条效果

    在正式开始之前,先上个效果图看看:

    很酷炫有木有???

    那么如何实现这个效果呢?

    首先,我做这个特效的基本步骤是这样的:

    1.将若干个粒子随机分布在画布(canvas)上,并且给他们一个初始速度

    2.为了不让粒子离开画布的可视范围,当粒子移动到画布边缘时,会进行反弹

    3.设定一个最大距离,若粒子间的距离超过最大距离,便不会产生连线,若小于等于最大距离,则粒子间距越小,粒子间连的线就越宽,或者说颜色越深

    4.设定鼠标事件,当鼠标移动时,粒子与鼠标距离若小于等于最大距离,那么粒子将会朝着鼠标方向移动

     

    那么我们就按照这四个步骤来进行:

    1 <canvas width="1300" height="600" id="myCanvas"></canvas>

    正常操作,我设置了一个宽1300,高600的画布

    1 let c = document.getElementById('myCanvas');
    2 let cxt = c.getContext('2d');
    3 let num = 100;//粒子数量
    4 let balls = [];

    num是我设置的粒子数量,数量可以随意设置,但要注意的是,如果粒子数量太多,因为设备的原因,可能粒子不能够流畅运动,或移动的很慢。

    用balls数组来存放随机数

     1 for(let i = 0; i < num; i ++){
     2     //设置随机数(粒子横坐标,纵坐标,半径,水平移动速度,垂直移动速度)
     3     let startX = Math.random() * (c.width - 5) + 5;
     4     let startY = Math.random() * (c.height - 5) + 5;
     5     let radius = Math.random() * 2 + 1;
     6     let speedX = Math.random() * 2 - 1;
     7     let speedY = Math.random() * 2 - 1;//速度的方向(正负)大小都是随机的
     8     // let colour = `rgba(${Math.random() * 255},${Math.random() * 255},${Math.random() * 255},.7)`;
     9     balls.push({startX, startY, radius, speedX, speedY});//存入数组
    10 }

    接下来用for循环设置随机数

    从上到下顺序是:粒子初始横坐标、纵坐标、半径、水平移动速度、和垂直移动速度

    还可以设置粒子的颜色,这里我注释掉了

    最后把随机出的数推入数组储存起来

     1 function change(ball){
     2     cxt.beginPath();//开始绘制粒子
     3     cxt.fillStyle = '#000';
     4     cxt.arc(ball.startX, ball.startY, ball.radius, 0, Math.PI * 2);
     5     cxt.fill();
     6 
     7    //粒子碰到画布边界会自动反弹   
     8    ball.speedX *= (ball.startX + ball.radius) >= c.width || (ball.startX - ball.radius) <= 0 ? -1 : 1;
     9     ball.speedY *= (ball.startY + ball.radius) >= c.height || (ball.startY - ball.radius) <= 0 ? -1 : 1;   
    10      
    11   ball.startX += ball.speedX;
    12     ball.startY += ball.speedY;
    13 }

    先绘制粒子,然后判断粒子是否运动到画布边界,如果是,就使速度乘以-1,这样粒子会朝着相反方向运动

    注意:这里不能先执行 startX/Y + speedX/Y 再执行粒子是否运动到画布边界的判断

    如果这样做,那么在设置初始横坐标、纵坐标的时候就不能直接设为 Math.random() * c.width/height 或 Math.random() * c.width/height + 1

    因为Math.random()随机出的数是包含0的,如果粒子的初始横坐标或纵坐标就在画布边缘,那么粒子会在边缘快速的反弹,不会移动到画布中,当然如果你有别的方法,那么请无视我这句话

    function move(){
        cxt.clearRect(0, 0, c.width, c.height);//清除画布
        for(let i = 0; i < num; i ++){
            change(balls[i]);
        }
    }

    创建一个move函数来调用change

    每当执行一遍for循环时,都要清除一次画布,不然之前粒子运动的轨迹都会显示在上面

    //点与点之间划线,距离较小的连线,距离大的则不连
        function drawLines(){
            move();
            let long = 100;
            let newBalls=[];//设置新数组以便与其他粒子比较距离
    
            balls.forEach(function(ball){//遍历数组
                for(let i = 0; i < num; i ++){
                    newBalls[i] = balls[i];//赋值给新数组
                    let disX = newBalls[i].startX - ball.startX;
                    let disY = newBalls[i].startY - ball.startY;
                    let dis = Math.sqrt(disX * disX + disY * disY);
                    if(dis <= long){
                        cxt.beginPath();
                        cxt.lineWidth = (long - dis) / long;
                        cxt.strokeStyle = `rgba(0, 0, 0, ${dis / long})`;
                        cxt.moveTo(newBalls[i].startX, newBalls[i].startY);
                        cxt.lineTo(ball.startX, ball.startY);
                        cxt.stroke();
                    }
                }
                //添加鼠标事件
                c.onmousemove=function(ev){
                    ev = event || window.event;
                    for(let i = 0; i < num; i ++){
                        let newX =  newBalls[i].startX - ev.clientX;
                        let newY =  newBalls[i].startY - ev.clientY;
                        let newDis = Math.sqrt(newX * newX + newY * newY);
                        if(newDis <= long){//周围的点朝着鼠标方向移动
                            newBalls[i].startX -= newX * 0.04;
                            newBalls[i].startY -= newY * 0.04;
                        }
                    }
                };
            });
        }
        setInterval(drawLines,20);

    创建一个drawline函数来执行连线效果和鼠标移动效果

    首先设置一个最大距离long,我设的是100px的长度

    定义一个新数组newBalls[]便于和balls[]中的粒子做距离比较

    用foreach()方法遍历balls数组,这样就可以直接和balls的所有粒子进行比较,比较方便

    计算出粒子之间的距离dis,如果dis小于等于long,那么就绘制线条连接两个粒子

    if(dis <= long){
          cxt.beginPath();
          cxt.lineWidth = (long - dis) / long;
          cxt.strokeStyle = `rgba(0, 0, 0, ${dis / long})`;
          cxt.moveTo(newBalls[i].startX, newBalls[i].startY);
          cxt.lineTo(ball.startX, ball.startY);
          cxt.stroke();
     }

    线条的宽度和透明度是根据long和dis的关系来设置的

    这样的效果更加自然

     1 c.onmousemove=function(ev){
     2      ev = event || window.event;
     3      for(let i = 0; i < num; i ++){
     4         let newX =  newBalls[i].startX - ev.clientX;
     5         let newY =  newBalls[i].startY - ev.clientY;
     6         let newDis = Math.sqrt(newX * newX + newY * newY);
     7         if(newDis <= long){//周围的点朝着鼠标方向移动
     8             newBalls[i].startX -= newX * 0.04;
     9             newBalls[i].startY -= newY * 0.04;
    10         }
    11     }
    12 };

    设置鼠标事件,仍然是一样的操作

    先算出鼠标和粒子之间的距离,如果粒子和鼠标距离小于等于最大距离,就朝着鼠标的方向移动

    这个效果是我自己做的,和网上的很多效果感觉不太一样,如果发现了更酷炫的效果,那么之后我会更新

  • 相关阅读:
    合并区间
    判断字符串是否是IP
    Python -- 异常处理
    python -- 双下方法
    python -- 判断函数和方法
    python -- 面向对象:反射
    Python -- 面向对象:类的成员
    Python -- 面向对象:类的约束
    Python -- 面向对象的三大特性及深入了解super()
    Python -- mro算法
  • 原文地址:https://www.cnblogs.com/FrankLongger/p/9591800.html
Copyright © 2011-2022 走看看