ZRender实现粒子网格动画实战

来源:互联网 发布:电车难题知乎 编辑:程序博客网 时间:2024/05/16 07:26

注:本博文代码基于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 国际许可协议进行许可。

2 0
原创粉丝点击