canvas动画实例初探

运维门户中心是一个集组织管理、应用管理、栏目订阅等功能的综合平台,登录界面一直比较朴素,为了美化登录界面需要选取背景。参考了一些线上门户的登录背景,比如Google的背景是用svg写的,知乎是用canvas写的。比较起来,canvas写的动画视觉效果更好,决定学习下知乎背景的写法。

整体思路

获取画布渲染上下文,生成制定个数的随机坐标点,给每个点不同的初始速度使之运动。在两两点之间建立联系,给连线一个透明度值,使两点越近连线越明显,以符合日常生活经验。根据速度改变坐标,遇到屏幕边界时,做穿越或碰撞处理。在浏览器重绘之前更新画布。

核心分解

html内容只有一行<canvas id="myCanvas"></canvas><canvas> 是 HTML5 新增的元素,可通过JavaScript脚本来绘制图形。它的用途广泛,可用于绘制图形,制作照片,创建动画,甚至可以进行实时视频处理或渲染。下面来分解js的内容。

canvas基础

<canvas>标签只有两个属性,width与height,这里通过DOM properties设置了宽度和高度。canvas起初是空白的,为了展示,脚本要先找到渲染上下文(the rendering context),然后在它上面绘制。<canvas>元素的getContext()方法是用来获取渲染上下文和它的绘画功能,传入的参数”2d”是上下文的格式。

接下来是定义圆形的半径,取[5,20]范围,初始位置在不出画布的前提下随机,初始水平速度与垂直速度在[-0.4,0.4]范围内。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var w = window.innerWidth;
var h = window.innerHeight;
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var allRound = [];
var dxdy = [];
//设置画布宽高与窗口宽高一样
canvas.width = w;
canvas.height = h;
function fnRandom(min, max) {
return parseInt((max - min) * Math.random() + min + 1);
}

function Round() {
this.r = fnRandom(5, 20);
this.diam = this.r * 2;
//随机位置
var x = fnRandom(0, canvas.width - this.r);
this.x = x < this.r ? this.r : x;
var y = fnRandom(0, canvas.height - this.r);
this.y = y < this.r ? this.r : y;
//随机速度
var speed = fnRandom(2, 4) / 10
this.speedX = fnRandom(0, 4) > 2 ? speed : -speed;
this.speedY = fnRandom(0, 4) > 2 ? speed : -speed;
//颜色
this.color = "#ECEEEF";
}

路径绘制的方法

Round有两个方法draw与move,draw方法是绘制圆形,这里介绍下使用路径绘制图形的步骤:

  1. 首先,创建路径的起始点。
  2. 使用画图命令画出路径。
  3. 把路径封闭。
  4. 可通过描边或填充路径区域来渲染图形。

以下是用到的函数:

  1. beginPath()新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。
  2. closePath()闭合路径之后图形绘制命令又重新指向到上下文中。
  3. stroke()通过线条来绘制图形轮廓。
  4. fill()通过填充路径的内容区域生成实心的图形。

move方法是圆形从现有坐标上移动一个水平和垂直位移。提供圆形到达画布边界的处理方案:如果圆形碰到右侧边界,则水平速度取反向,模拟碰撞反弹的效果;如果圆形碰到左侧边界,则将圆形置于画布右侧,模拟圆形穿过左侧边界的效果。画布的下边界与上边界也分别模拟碰撞反弹与穿越效果。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Round.prototype.draw = function() {
//绘制函数
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
}
Round.prototype.move = function() {
this.x += this.speedX;
if(this.x > canvas.width - this.r) {
this.speedX *= -1;
} else if(this.x < this.r) {
this.x = canvas.width - this.r;
}
this.y += this.speedY;
if(this.y > canvas.height - this.r) {
this.speedY *= -1;
} else if(this.y < this.r) {
this.y = canvas.height - this.r;
}
}

动画基本步骤

roundMove()是核心部分,是它让动画动了起来,先看下动画的基本步骤:

  1. 清空canvas 这里用clearRect方法实现。
  2. 保存canvas状态 如果你要改变一些会改变canvas状态的设置,又要在每画一帧之时都是原始状态则要先保存。
  3. 绘制动画图形 这一步是重绘动画帧。这里用到的是window.requestAnimationFrame(callback),这个方法提供了平缓有效率的方式执行动画,当系统准备好重绘条件的时候次啊会调用绘制动画帧,在重绘之前浏览器执行函数roundMove。
  4. 恢复canvas状态 如果已保存了canvas状态,可以先恢复,再重绘下一帧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//使用Round
function initRound() {
//初始化20个圆形对象,放到数组中
for(var i = 0; i < 20; i++) {
var obj = new Round();
allRound.push(obj);
}
}
initRound();
function roundMove() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
//遍历所有的圆形对象,让对象自己重绘,移动
for(var i = 0; i < allRound.length; i++) {
var round = allRound[i];
round.draw();
round.move();
dxdy[i] = {
dx: round.x,
dy: round.y
};
var dx = dxdy[i].dx;
var dy = dxdy[i].dy;
for(var j = 0; j < i; j++) {
var sx = dxdy[j].dx;
var sy = dxdy[j].dy;
var l = Math.sqrt((dx - sx) * (dx - sx) + (dy - sy) * (dy - sy));
var C = 1 / l * 7 - 0.009;
var o = C > 0.03 ? 0.03 : C;
ctx.strokeStyle = 'rgba(0,0,0,' + o + ')';
ctx.beginPath();
ctx.lineWidth = 2;
ctx.moveTo(dxdy[i].dx, dxdy[i].dy);
ctx.lineTo(dxdy[j].dx, dxdy[j].dy);
ctx.closePath();
ctx.stroke();
}
}
window.requestAnimationFrame(roundMove);
}
roundMove();

roundMove()中逐个生成圆形,且和在此之前生成的圆形两两之间连线,连线为黑色,rgba的最后一个参数用来设置透明度。如果不设置透明度,则默认透明度为1,则可以看到所有的连线。这里计算了两个圆形之间的距离l,透明度设置在(0,0.03]之间。距离越大,透明度越高,连线就越不清晰。距离一定远之后,看起来两个圆形就是离散的了。连线使用的就是路径绘制的方法。最后实现的效果,可以在运维门户中心的登录页面看到,传送门: http://10.142.90.58:9092/#/

参考资料

  1. 一个很好的canvas教程:https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API
  2. w3cSchool的canvas教程:http://www.w3school.com.cn/jsref/dom_obj_canvas.asp

事实上,知乎的登录页背景是直接用了particles.js库实现的,下面是相关教程:

  1. particles的demo:https://codepen.io/VincentGarreau/pen/pnlso
  2. particles的github:https://github.com/VincentGarreau/particles.js
为本少女加个鸡腿吧!
显示 Gitment 评论