您的位置:首页 > 其它

ZRender实现粒子网格动画实战

2017-04-24 15:57 274 查看
注:本博文代码基于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部分

这里主要用到的形状就是
Circle
Line
,先引入这两个组件:

['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 国际许可协议进行许可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐