一、动画
动画通过每帧放出不同的画面来实现,而每帧的需要的操作如下:
- 清空上一帧绘制的内容。
- 如果需要保存当前canvas状态(笔刷样式、变形)的话就将其保存一下。
- 绘制这一帧需要画的内容。
- 如果之后需要恢复之前保存的状态的话就恢复一下。
由于动画需要按时切换显示的内容,所以会使用到和时间相关的函数:
setInterval(callback, delay)setTimeout(callback, delay)requestAnimationFrame(callback)
由于setTimeout和setInterval有可能被其他语句耽误,通常使用requestAnimationFrame方法。
本博客banner的动画也是使用这个方法制作的,大致步骤如下:
1 | let length // 精灵图长度 |
商业中会使用Adobe Animate或Spine来绘制并导出canvas格式的动画,然后只要导入到页面中即可展示动画。
二、优化
双缓冲
在游戏内会经常使用双缓冲来提高性能,简单地说就是预先在另外一个离屏的canvas上绘制好大量复杂内容,然后在需要的时候直接把离屏canvas渲染到当前canvas中。
1
2
3
4
5
6
7
8
9let canvas // 当前canvas
ctx = canvas.getContext('2d')
let _canvas // 离屏canvas
_canvas = document.createElement('canvas')
_canvas.width = canvas.width
_canvas.height = canvas.height
const _ctx = _canvas.getContext('2d')
ctx.drawImage(_canvas, x, y, w, h)避免浮点数坐标
1
2// 当画一个没有整数坐标点的对象时会发生子像素渲染,浏览器为了达到抗锯齿的效果会做额外的运算。使用前请对坐标取整。
ctx.drawImage(myImage, 0.3, 0.5)在离屏canvas中缓存图片的不同尺寸,而不要用
drawImage()去缩放。对于具有不相关动画效果的内容,用多个canvas来展示:
比如很多游戏的角色游戏层、背景图、UI这三个内容分别有完全无关的动画效果,这时候可以配置三个canvas,只在这个图层需要的时候重绘。对于静态背景,甚至可以直接使用img标签或background代替。
用CSS的transform缩放画布:
transform使用GPU,因此速度更快。 最好的情况是不直接缩放画布,或者具有较小的画布并按比例放大,而不是较大的画布并按比例缩小。关闭透明度:
如果不需要背景透明的画布,那么当使用
canvas.getContext时把alpha选项设置为false。这个选项可以帮助浏览器进行内部优化。1
const ctx = canvas.getContext('2d', { alpha: false })
其他:
- 将画布的函数调用集合到一起(例如,画一条折线,而不要画多条分开的直线)。
- 避免不必要的画布状态改变。
- 渲染画布中的不同点,而非整个新状态。
- 尽可能避免
shadowBlur特性。 - 尽可能避免text rendering。
- 尝试不同的方法来清除画布(
clearRect()vs.fillRect()vs. 调整canvas大小)。 - 动画请使用
window.requestAnimationFrame()而非window.setInterval()。 - 谨慎使用大型物理库。