用 canvas 作个好玩的网站背景

  不知不觉又很久没更过博客了,老调新弹一下,以前作的一个小效果,以为蛮有意思的,也有朋友问是怎么作的,就分享一下,写个博文吧。html

  先上demo吧:http://whxaxes.github.io/canvas-test/src//Funny-demo/netparticle/net_1.html  git

  上面这个demo是最先写的,后来作了点小修改后就用到了本身的网站上当个banner,有兴趣的也能够看看效果:http://wanghx.cn/    ,至少同事说仍是挺酷炫的。这种效果其实很早以前就有了的,我也是在一个网站上看到相似的效果,发现这个创意不错,并且难度很小,就花了一个午休的时间写了一下。github

  接下来就分析一下怎么实现把。canvas

  这种效果一眼看过去,就知道其实就是一堆粒子在进行无序的运动。而后当粒子与粒子之间的距离小于必定值后,就进行连线,而且根据距离的大小来对线条的粗细进行一些更改,就能够作出这种有点像蛛网的感受了。原理很简单,直接上代码:数组

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})
  }

  首先,用一个数组,装载两百个分散在canvas各处的粒子对象,而且给每一个对象一个随机的运动趋势。也就是xa和ya,用于表示垂直和水平的运动趋势。其实就是一个用于每次循环的时候进行叠加的值。dom

  实例化好两百个粒子对象后。就可让他们开始运动:ide

    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);

  运动的逻辑也很简单,每次给粒子更新新的状态,其实就是根据此前初始化粒子的时候给予的xa和ya,进行一个累加,就能够造成运动的效果了。性能

  固然,粒子不能往一个方向无限的运动下去,因此咱们还须要判断粒子是否运动到边界了,若是运动到了边界,就把运动趋势进行反转。也就作出了一种粒子反弹的效果。上面的extendDis实际上是我为了让粒子反弹点在canvas外而定义的一个变量,用于控制粒子跑到离开canvas多远后才进行反弹。测试

  固然,每次运动完都对粒子进行一个绘制。这一段代码会放到一个叫move的function里。优化

  

  就上面的一些代码,就完成了粒子的初始化,以及运动了。接下来就是画线了。逻辑也很简单,就是遍历,逐个粒子计算距离,当两个比较的粒子之间的距离小于某个值,就进行画线。代码以下:

  /**
   * 逐个对比连线
   * @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);
    });
  }

  逻辑也比较简单,就是遍历数组,把遍历到的粒子跟其余粒子进行逐个比对。当距离小于上面的lineDis的时候,就进行连线。为了减小计算量,每次计算过的粒子将会从用于计算的ndots数组中删除,避免重复计算。同时若是两个粒子的垂直距离和水平距离大于lineDis,那也就不必再算两个粒子的距离了,直接不作处理,从而减小计算量。

  其实这个计算用的仍是所谓的笨方法,我此前有在想有什么更好的计算方法能更好的优化计算效率呢。而后想了一个方法而且进行了一个测试,就是先对粒子根据x轴进行快速排序,而后按顺序进行比较,当比较到的粒子的水平距离大于lineDis的时候,就不用再比下去了。由于后面的都确定会比当前粒子要更远,想着就按照这样会减小计算量应该会提高效率。可是我对两个不一样的计算方法都进行了耗时比较,结果仍是原来的笨方法的性能更优。由于这个新方法每次都要从新排序,这个计算量也是蛮大的。而后就暂时没想到其余了,若是读者有更好的idea不妨分享一下。

  

  同事有问我那个鼠标划过,粒子会聚起来的效果很神奇,怎么作的,其实这个效果比想象中简单不少,并且在上面的代码里我也给出来了。再给出一段保存鼠标位置的代码,很简单,就是鼠标移动的时候保存鼠标位置。

  // 鼠标活动时,获取鼠标坐标
  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;
  };

  保存了鼠标位置后,在每次动画循环的时候,把鼠标位置也当成一个粒子对象塞进数组进行比较:

  // 每一帧循环的逻辑
  function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

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

    RAF(animate);
  }

  而粒子往鼠标方向运动的代码,其实就这么一小截:

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

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

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

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

  这个demo是很早以前写的,跟上面贴出来的代码会有点出入,可是原理是同样的。懂了原理,就能够本身去实现一个了。

  

  若是以为demo不错,就在github给个star呗,固然也欢迎fork

相关文章
相关标签/搜索