前端动画(Canvas实现)
最近不知道为什么对于前端各种花里胡哨的UI交互灰常感兴趣,甚至花了9.9块专门去掘金买了本相关的小册子看,之后知道了这些花里胡哨的特效都是通过Canvas或者CSS本身的Animation来做到的,现在就简单地来讲讲这两种方法分别是怎么来做动画的
1.Canvas
1.1 什么是Canvas
在MDN中是这样定义canvas的:
canvas是 HTML5 新增的元素,可用于通过使用 JavaScript 中的脚本来绘制图形。例如,它可以用于绘制图形、
制作照片、创建动画,甚至可以进行实时视频处理或渲染。
我们可以认为 canvas标签只是一个矩形的画布。JavaScript 提供了一系列的API操作作为画笔,负责在画布上画画。
1.2 Canvas常用操作
既然说了JavaScript提供了一系列的API操作作为画笔,那么就要简单的来讲讲都有哪些常用的API了
首先既然我们要在画布上绘图,那么我们总得有一个画布吧,所以常常是先在html里声明一个canvas标签然后通过document.getElementById的方式得到对应的Canvas,然后再通过getContext(“2d”)得到对应的可绘画的画布,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
<script>
/** @type {HTMLCanvasElement} */
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
WIDTH = document.documentElement.clientWidth;
HEIGHT = document.documentElement.clientHeight;
canvas.width = WIDTH;
canvas.height = HEIGHT;
var gar = context.createLinearGradient(0, 0, 500, 0)
gar.addColorStop(0, '#4CB8C4')
gar.addColorStop(1, '#3CD3AD')
context.fillStyle = gar
context.fillRect(0, 0, WIDTH, HEIGHT)
</script>
</html>
在上面的代码里整了一个以渐变色为背景的画布并作为了网页的背景,当然上述效果也可以单纯地通过CSS来实现,如下:
<style>
html, body {
margin: 0;
height: 100%;
width: 100%;
background: linear-gradient(#4CB8C4, #3CD3AD);
overflow: hidden;
</style>
既然是画布,那么肯定不能单纯地整个背景就好了的,肯定是要在上面画画的,所以接下来简单的罗列一些图形以及路径的API
| 方法 | 描述 |
|---|---|
| fill() | 填充由路径围起来的区域 |
| stroke() | 将边给描绘出来 |
| arc() | 创建圆弧,常常也用来创建圆 |
| rect() | 创建矩形 |
| fillRect() | 填充矩形路径区域 |
| strokeRect() | 绘制矩形路径描边 |
| clearRect() | 在给定的矩形内清除指定的像素,常常用来清屏 |
| beginPath() | 起始一条路径,或重置当前路径 |
| moveTo() | 把路径移动到画布中的指定点,不创建线条 |
| lineTo() | 添加一个新点,然后在画布中创建从该点到最后指定点的线条 |
| closePath() | 创建从当前点回到起始点的路径 |
之前只是单纯的把图形勾勒了出来,但是具体用什么颜色,以及怎么填充都还没有提到,所以接下来罗列一下颜色、样式和阴影
| 属性 | 描述 |
|---|---|
| fillStyle | 设置或返回用于填充绘画的颜色、渐变或模式 |
| strokeStyle | 设置或返回用于笔触的颜色、渐变或模式 |
| shadowColor | 设置或返回用于阴影的颜色 |
| shadowBlur | 设置或返回用于阴影的模糊级别 |
通过阴影颜色和模糊,我们可以画出一个小月亮!
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #000000;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
<script>
/** @type {HTMLCanvasElement} */
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
WIDTH = document.documentElement.clientWidth;
HEIGHT = document.documentElement.clientHeight;
canvas.width = WIDTH;
canvas.height = HEIGHT;
context.beginPath()
context.arc(200, 200, 100, 0, 2 * Math.PI, true)
context.fillStyle = "#fff"
context.shadowColor = "#fff"
context.shadowBlur = 50
context.fill()
context.closePath()
</script>
</html>

说到这里,其实Canvas的常规操作就已经差不多了,总结一下就是:

1.3动画
但是说了这么多,动画呢,动画呢,动画呢?
动画从本质上就是在每一帧将之前的画的东西都擦掉,然后再画上新的东西,类似小人书那样,只要我翻得足够快,那么小人的动作就能连起来,变成动画
所以所有的基于Canvas的动画都是要么通过计时器,要么通过requestAnimationFrame这个函数来搞定的,而且自从有了requestAnimationFrame函数以后,计时器也很少用来做动画了,那么这个函数究竟是做什么的呢
window.requestAnimationFrame() 方法告诉浏览器,你希望执行动画,并请求浏览器调用指定的函数在下一次重
绘之前更新动画。该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。
requestAnimationFrame() 函数可以说是专门用来写动画的。那么 requestAnimationFrame() 有什么优点呢?
编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平
滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化。
大多数电脑显示器的刷新频率是 60Hz,大概相当于每秒钟重绘 60 次。大多数浏览器都会对重绘操作加以限制,不
超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是
1000ms/60,约等于 16.6ms。
requestAnimationFrame 采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开
销;也不会因为间隔时间太长,使动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统
资源,提高系统性能,改善视觉效果。
所以我们就使用requestAnimationFrame()函数递归地调用animate()函数来实现动画的功能
星星上升的代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
html, body {
margin: 0;
overflow: hidden;
width: 100%;
height: 100%;
cursor: none;
background: black;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
var ctx = document.getElementById('canvas'),
content = ctx.getContext('2d'),
round = [],
WIDTH,
HEIGHT,
initRoundPopulation = 80;
WIDTH = document.documentElement.clientWidth;
HEIGHT = document.documentElement.clientHeight;
ctx.width = WIDTH;
ctx.height = HEIGHT;
function Round_item(index, x, y) {
this.index = index;
this.x = x;
this.y = y;
this.r = Math.random() * 2 + 1;
var alpha = (Math.floor(Math.random() * 10) + 1) / 10 / 2;
this.color = "rgba(255,255,255," + alpha + ")";
}
Round_item.prototype.draw = function () {
content.fillStyle = this.color;
content.shadowBlur = this.r * 2;
content.beginPath();
content.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
content.closePath();
content.fill();
};
function animate() {
content.clearRect(0, 0, WIDTH, HEIGHT);
for (var i in round) {
round[i].move();
}
requestAnimationFrame(animate)
}
Round_item.prototype.move = function () {
this.y -= 0.15;
if (this.y <= -10) {
this.y = HEIGHT + 10;
}
this.draw();
};
function init() {
for (var i = 0; i < initRoundPopulation; i++) {
round[i] = new Round_item(i, Math.random() * WIDTH, Math.random() * HEIGHT);
round[i].draw();
}
animate();
}
init();
</script>
</body>
</html>
1.4鼠标和屏幕互动
不管是在Windows还是在别人的网页上,我们往往可以看到一些随着鼠标而出现的特效,像什么鼠标后面带着一圈波纹啊,鼠标引起粒子之间互相连接啊什么的,这在过去几年里就很火,因为好tm酷炫啊!
然而这些本质上其实也就是动画啦,顺便监听了鼠标的移动而已。
它的流程就是监听鼠标的移动,并且在每次移动的时候都把鼠标对应的位置push到一个全局的数组里,然后在每一帧的时候通过这个全局的坐标数组来实现绘画粒子以及粒子的移动和小时,除此之外,在每一次鼠标移动的时候都重新开启一个定时器,从而保证在鼠标一段时间不动的话就啥都不做。在这之上那些基于鼠标运动而生成的粒子都最好有一个随时间移动并过了一段时间消失也即把Alpha设为0的动态效果
代码大概200多行,因为太长,就不发了,这里留一个gitee地址 星空鼠标
1.5 总结
怎么样才能做出一个酷炫的背景呢,总结如下:
- 背景颜色不宜过多
- 粒子数量多
- 粒子在动
- 能和鼠标进行交互