ZRender實現粒子網格動畫實戰


注:本博文代碼基於ZRender 3.4.3版本號開發,相應版本號庫地址:ZRender 庫


效果


實現分析

通過上面顯示的效果圖,能夠看出,這樣的效果就是在Canvas中生成多個可移動的點,然后依據點之間的距離來確定是否連線。思路比較簡單。

實現問題:

  • 保持Canvas 100%顯示
  • resize時。自己主動調節Canvas尺寸和內部變量
  • 生成圓點
  • 實現圓點的移動,及邊界處理
  • 實現原點的直線連接

Canvas設置

html:

<canvas id="main"></canvas>

css:

#main{ position: absolute; //用於100%填充 left:0; top:0; background: #000; z-index: -1; //方便做背景層使用 }

ZRender部分

這里主要用到的形狀就是CircleLine。先引入這兩個組件:

['zrender',
    'zrender/graphic/shape/Circle',
    'zrender/graphic/shape/Line'], 
function(zrender, Circle, Line){}

設置全局及配置項用到的變量

var winH = window.innerHeight; //同步頁面寬、高
var winW = window.innerWidth; //同步頁面寬、高

var opts = { //可配置參數
    background: '#000', //Canvas背景色
    paricalRadius: 2, //粒子半徑
    paricalColor: 'rgb(0, 255, 0)', //粒子顏色
    lineColor: 'rgb(0, 255, 0)', //連線顏色
    joinLineDis: 300, //粒子間連線的要求距離
    particalAmount: 30, //生成的粒子數量
    speed: 1, //粒子速度
};
var tid; //setTimeout id,防抖處理
var particals = []; //用於存儲partical對象

初始化ZRender

var zr= zrender.init(main, {width: winW, height: winH});

zr.dom.style.backgroundColor = opts.background; //設置背景色

窗體 resize 處理

window.addEventListener('resize', function(){
    clearTimeout(tid);

    var tid = setTimeout(function(){ //防抖處理
        winW = zr.dom.width = window.innerWidth;
        winH = zr.dom.height = window.innerHeight;

        zr.refresh();
    }, 300); //這里設置了300ms的防抖間隔
}, false);

效果:



創建粒子類 Partical

總結一下這個類,須要下面屬性:

  • 坐標位置 x, y
  • 粒子速度
  • 粒子移動角度
  • 粒子顏色
  • 粒子半徑
  • 粒子的角度方向變量
  • 粒子的ZRender形狀實例

方法:

  • 更新位置坐標
  • 划線

這邊直接用ES6的語法來創建類:

class Partical {}

構造器:

constructor(){
    this.lines = [], //用於存儲連線
    //粒子坐標初始化
    this.x = winW * Math.random();
    this.y = winH * Math.random();
    this.speed = opts.speed + Math.random(); //這個random可不加,主要是為了制作不同的速度的
    this.angle = ~~(360 * Math.random());
    this.color = opts.paricalColor;
    this.radius = opts.paricalRadius + Math.random();
    this.vector = {
        x: this.speed * Math.cos(this.angle),
        y: this.speed * Math.sin(this.angle),
    } 
    this.element = new Circle({
        shape: {
            cx: this.x,
            cy: this.y,
            r: this.radius,
        },
        style: {
            fill: this.color,
        }
    });
};

更新位置坐標方法:

updatePosition(){
    //邊界推斷
    if(this.x >= winW || this.x <= 0){
        this.vector.x *= -1;
    }

    if(this.y >= winH || this.y <= 0){
        this.vector.y *= -1;
    }

    if(this.x > winW){
        this.x = winW;
    }

    if(this.x < 0){
        this.x = 0;
    }

    if(this.y > winH){
        this.y = winH;
    }

    if(this.y < 0){
        this.y = 0;
    }

    //更新位置坐標
    this.x += this.vector.x;
    this.y += this.vector.y;

    //更新形狀坐標
    this.element.shape.cx = this.x;
    this.element.shape.cy = this.y;

    this.element.dirty();
};

划線方法:

drawLines(){
    //清空lines。用於重繪線
    for(let i = 0; i < this.lines.length; i ++){
        let l = this.lines[i];

        zr.remove(l); //刪除形狀
        l = null; //並解除綁定
    }
    this.lines = []; //刪除后。清空數組

    //遍歷各個點之間的距離
    for(let i = 0; i < particals.length; i ++){
        let p = particals[i];

        //勾股定理,獲取兩點之間的距離
        let distance = Math.sqrt(Math.pow(this.x - p.x, 2) + Math.pow(this.y - p.y, 2));

        if(distance <= opts.joinLineDis && distance > 0){
            let opacity = 1 - distance / opts.joinLineDis; //依據距離大小來設置透明度
            let color = opts.lineColor.match(/\d+/g); //由於這里要用到透明度。所以須要又一次組合rgba。先把各個顏色值取到數組中

            let l = new Line({
                 shape: {
                    x1: this.x,
                    y1: this.y,
                    x2: p.x,
                    y2: p.y,
                },

                style: {
                    stroke: 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + opacity + ')', //組建顏色
                    fill: null
                },
            });

            this.lines.push(l); //存入lines
            zr.add(l); //增加ZRender Storage中
        }
    };
}

眼下所有核心部分已完畢,如今來初始化它:


var init = function(){
    for (let i = 0; i < opts.particalAmount; i++) {
        let p = new Partical();

        particals.push(p); // 把粒子實例 存入particals中,方便后面操作
        zr.add(p.element); //增加 ZRender Storage中
    }
};

效果:



開始動畫函數,讓粒子動起來,並生成連接線:

function loop(){
    for(let i = 0; i < particals.length; i ++){
        let p = particals[i];

        p.updatePosition(); //更新位置
        p.drawLines(); //繪制線段
    }

    window.requestAnimationFrame(loop);
};

終於效果:


所有代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="./esl.js"></script>
    <style> #main{ position: absolute; left:0; top:0; background: #000; z-index: -1; } </style>
</head>
<body>
    <canvas id="main"></canvas>

<script> require.config({ packages:[ { name: 'zrender', location: './src', main: 'zrender', }, ], }); require(['zrender', 'zrender/graphic/shape/Circle', 'zrender/graphic/shape/Line'], function(zrender, Circle, Line){ /* * 作者:王樂平 * 博客:http://blog.csdn.net/lecepin/ */ //-----全局var-----{ var winH = window.innerHeight; var winW = window.innerWidth; var opts = { background: '#000', //Canvas背景色 paricalRadius: 2, paricalColor: 'rgb(0, 255, 0)', lineColor: 'rgb(0, 255, 0)', joinLineDis: 300, particalAmount: 30, speed: 1, }; var tid; //setTimeout id。防抖處理 var particals = []; //用於存儲partical對象 //-----------------} var zr = zrender.init(main, {width: winW, height: winH}); zr.dom.style.backgroundColor = opts.background; window.addEventListener('resize', function(){ clearTimeout(tid); var tid = setTimeout(function(){ winW = zr.dom.width = window.innerWidth; winH = zr.dom.height = window.innerHeight; zr.refresh(); }, 300); //這里設置了300ms的防抖間隔 }, false); class Partical { constructor(){ this.lines = [], //用於存儲連線 //粒子坐標初始化 this.x = winW * Math.random(); this.y = winH * Math.random(); this.speed = opts.speed + Math.random(); //這個random可不加,主要是為了制作不同的速度的 this.angle = ~~(360 * Math.random()); this.color = opts.paricalColor; this.radius = opts.paricalRadius + Math.random(); this.vector = { x: this.speed * Math.cos(this.angle), y: this.speed * Math.sin(this.angle), } this.element = new Circle({ shape: { cx: this.x, cy: this.y, r: this.radius, }, style: { fill: this.color, } }); }; updatePosition(){ if(this.x >= winW || this.x <= 0){ this.vector.x *= -1; } if(this.y >= winH || this.y <= 0){ this.vector.y *= -1; } if(this.x > winW){ this.x = winW; } if(this.x < 0){ this.x = 0; } if(this.y > winH){ this.y = winH; } if(this.y < 0){ this.y = 0; } this.x += this.vector.x; this.y += this.vector.y; this.element.shape.cx = this.x; this.element.shape.cy = this.y; this.element.dirty(); }; drawLines(){ //清空lines for(let i = 0; i < this.lines.length; i ++){ let l = this.lines[i]; zr.remove(l); l = null; } this.lines = []; //遍歷各個點之間的距離 for(let i = 0; i < particals.length; i ++){ let p = particals[i]; //勾股定理 let distance = Math.sqrt(Math.pow(this.x - p.x, 2) + Math.pow(this.y - p.y, 2)); if(distance <= opts.joinLineDis && distance > 0){ let opacity = 1 - distance / opts.joinLineDis; let color = opts.lineColor.match(/\d+/g); let l = new Line({ shape: { x1: this.x, y1: this.y, x2: p.x, y2: p.y, }, style: { stroke: 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + opacity + ')', fill: null }, }); this.lines.push(l); zr.add(l); } }; } } var init = function(){ for (let i = 0; i < opts.particalAmount; i++) { let p = new Partical(); particals.push(p); zr.add(p.element); } }; function loop(){ for(let i = 0; i < particals.length; i ++){ let p = particals[i]; p.updatePosition(); p.drawLines(); } window.requestAnimationFrame(loop); }; init(); loop(); }); </script>
</body>
</html>

博客名稱:王樂平博客

CSDN博客地址:http://blog.csdn.net/lecepin

知識共享許可協議
本作品採用 知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM