用 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

相關文章
相關標籤/搜索