Canvas realizes cool special effects of particle lines

Use canvas to be a fun website background

  Unconsciously, I haven’t updated my blog for a long time. I’ll play the old tune again. I did a small effect before, I think it’s quite interesting, and some friends ask how I did it, so I’ll share it and write a blog post.

  Let's go to the demo first: http://whxaxes.github.io/canvas-test/src//Funny-demo/netparticle/net_1.html  

  The demo above was written first, and after some minor modifications, it was used as a banner on my own website. If you are interested, you can also see the effect: http://wanghx.cn/     , at least my colleagues say it is still pretty cool of. This effect actually existed a long time ago. I also saw a similar effect on a website. I found that this idea is good and the difficulty is very small, so I spent a lunch break to write it.

  Next, we will analyze how to implement it.

  At a glance, this effect shows that it is actually a bunch of particles moving in disorder. Then when the distance between the particles is less than a certain value, the connection is made, and the thickness of the line is changed according to the size of the distance, so that this kind of feeling a bit like a spider web can be made. The principle is very simple, go directly to the code:

var dots = [];
  for (var i = 0; i < 200; i++) {
    var x = Math.random() * (canvas.width + 2*extendDis) - extendDis;
    var y = Math.random() * (canvas.height +  2*extendDis) - extendDis;
    var xa = (Math.random() * 2 - 1)/1.5;
    var ya = (Math.random() * 2 - 1)/1.5;

    dots.push({x, y, xa, ya})
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

  First, use an array with 200 particle objects scattered around the canvas, and give each object a random tendency to move. That is, xa and ya, used to represent vertical and horizontal motion trends. In fact, it is a value that is used to stack each time through the loop.

  After instantiating two hundred particle objects. Just get them to start exercising:

  dot.x += dot.xa;
    dot.y += dot.ya;

    // 遇到边界将速度反向
    dot.xa *= (dot.x > (canvas.width + extendDis) || dot.x < -extendDis) ? -1 : 1;
    dot.ya *= (dot.y > (canvas.height + extendDis) || dot.y < -extendDis) ? -1 : 1;

    // 绘制点
    ctx.fillStyle = `rgba(${rgb},${rgb},${rgb},1`;
    ctx.fillRect(dot.x - 0.5, dot.y - 0.5, 1, 1);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
The logic of motion is also very simple. Every time a particle is updated to a new state, it is actually an accumulation of the xa and ya given when the particle was initialized before, and the effect of motion can be formed.

  Of course, the particle cannot move infinitely in one direction, so we also need to determine whether the particle has moved to the boundary, and if it has moved to the boundary, reverse the movement trend. It also makes a kind of particle bounce effect. The extendDis above is actually a variable I defined to make the particle bounce point outside the canvas, which is used to control how far the particle runs away from the canvas before it bounces.

  Of course, after each movement, the particles are drawn one by one. This piece of code will be put into a function called move.

  

  With the above code, the initialization and movement of the particles are completed. Next is the line drawing. The logic is also very simple, that is to traverse, calculate the distance one by one, and draw a line when the distance between the two compared particles is less than a certain value. code show as below:

  /**
   * 逐个对比连线
   * @param ndots
   */
  function bubDrawLine(ndots){
    var ndot;

    dots.forEach(function (dot) {

      move(dot);

      // 循环比对粒子间的距离
      for (var i = 0; i < ndots.length; i++) {
        ndot = ndots[i];

        if (dot === ndot || ndot.x === null || ndot.y === null) continue;

        var xc = dot.x - ndot.x;
        var yc = dot.y - ndot.y;

        // 如果x轴距离或y轴距离大于max,则不计算粒子距离
        if(xc > ndot.max || yc > lineDis) continue;

        // 两个粒子之间的距离
        var dis = xc * xc + yc * yc;

        // 如果粒子距离超过max,则不做处理
        if( dis > lineDis ) continue;

        // 距离比
        var ratio;

        // 如果是鼠标,则让粒子向鼠标的位置移动
        if (ndot === warea && dis < 20000) {
          dot.x -= xc * 0.01;
          dot.y -= yc * 0.01;
        }

        // 计算距离比
        ratio = (lineDis - dis) / lineDis;

        // 粒子间连线
        ctx.beginPath();
        ctx.lineWidth = ratio / 2;
        ctx.strokeStyle = `rgba(${rgb},${rgb},${rgb},${ratio + 0.2}`;
        ctx.moveTo(dot.x, dot.y);
        ctx.lineTo(ndot.x, ndot.y);
        ctx.stroke();
      }

      // 将已经计算过的粒子从数组中删除
      ndots.splice(ndots.indexOf(dot), 1);
    });
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
The logic is also relatively simple, which is to traverse the array and compare the traversed particles with other particles one by one. When the distance is less than the above lineDis, the connection is made. In order to reduce the amount of calculation, each calculated particle will be removed from the ndots array used for calculation to avoid repeated calculation. At the same time, if the vertical and horizontal distances of the two particles are greater than lineDis, then there is no need to calculate the distance between the two particles, and no processing is performed directly, thereby reducing the amount of calculation.

  In fact, this calculation still uses the so-called stupid method. I was thinking about what better calculation method can better optimize the calculation efficiency. Then I thought of a method and conducted a test, which is to quickly sort the particles according to the x-axis, and then compare them in order. When the horizontal distance of the compared particles is greater than the lineDis, there is no need to compare them any more. Because the latter will definitely be farther than the current particle, thinking that it will reduce the amount of calculation and improve efficiency. But I did a time-consuming comparison of two different calculation methods, and the result was that the original stupid method performed better. Because this new method has to be reordered every time, the amount of calculation is quite large. Then I didn't think of anything else for the time being. If readers have better ideas, they might as well share them.

  

  A colleague asked me about the magic effect of particles converging when the mouse is swiped, how to do it, in fact, this effect is much simpler than expected, and I also gave it in the above code. Give another piece of code to save the mouse position. It is very simple, that is, save the mouse position when the mouse moves.

  // 鼠标活动时,获取鼠标坐标
  var warea = {x: null, y: null};
  var animateHeader = document.getElementById("animateHeader");

  animateHeader.onmousemove = function (e) {
    e = e || window.event;

    warea.x = e.clientX + 10;
    warea.y = e.clientY;
  };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
After saving the mouse position, in each animation loop, the mouse position is also treated as a particle object and stuffed into the array for comparison:
  // 每一帧循环的逻辑
  function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    bubDrawLine([warea].concat(dots));

    RAF(animate);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

And the code for the particle to move in the direction of the mouse is actually a small section:

  // 如果是鼠标,则让粒子向鼠标的位置移动
  if (ndot === warea && dis < 20000) {
    dot.x -= xc * 0.01;
    dot.y -= yc * 0.01;
  }
  • 1
  • 2
  • 3
  • 4
  • 5

 计算鼠标与粒子的距离,当鼠标与粒子之间的距离小于一定的时候,把粒子的位置更新为 “当前位置 - 鼠标粒子距离 * 0.01”即可。然后就会形成粒子往鼠标位置移动的效果了。

  整个效果就这样完成了,很简单,也很有意思,有兴趣的可以去研究一下发掘一些更好玩的效果。

  贴上这个demo的github地址:https://github.com/whxaxes/canvas-test/tree/gh-pages/src//Funny-demo/netparticle  

  这个demo是很早之前写的,跟上面贴出来的代码会有点出入,但是原理是一样的。懂了原理,就可以自己去实现一个了。

  

  如果觉得demo不错,就在github给个star呗,当然也欢迎fork

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324733401&siteId=291194637