• 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, { 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, { 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 国际许可协议进行许可。

  • 相关阅读:
    2016年 河南工业大学校赛 D题.rqy的键盘
    2016年 河南工业大学校赛 C题.魔法宝石
    jqueryMobile导航
    jqueryMobile列表
    jqueryMobile
    停止css3动画
    导航条
    移动端前面必须加的两行代码
    标签页
    file上传图片预览(此方法支持app)
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8663329.html
Copyright © 2020-2023  润新知