Canvas-性能优化
性能
1, 在离屏 canvas 上预渲染相似的图形或者重复的对象
每一帧里有好多复杂的图像生成运算,考虑创建一个离屏 canvas 将该图像画在 canvas 上,或者图像每次改变画一次,然后在每帧上画出视线以外的这个画布
canvas 上直接绘制线条会比离屏渲染性能要好。render-vs-prerender
canvas 预渲染画布大小要紧贴原绘画的内容大小,这样性能会好很多,如果预渲染画布大小比要绘制内容大很多,这样会浪费预渲染的性能,否则,render-vs-prerender
2,避免浮点数的坐标,用整数代替
当画一个没有整数坐标点的对象时会发生子像素渲染
context.drawImage(myImage, 0.3, 0.5)
// With a bitwise or.
rounded = (0.5 + somenum) | 0;
// A double bitwise not.
rounded = ~~ (0.5 + somenum);
// Finally, a left bitwise shift.
rounded = (0.5 + somenum) << 0;
浏览器为了达到抗锯齿的效果会做额外的运算处理,避免调用 drawImage() 函数时使用浮点坐标值, 情况使用 Math.floor() 将坐标取整
3,避免在 drawImage 时缩放图片操作
在离屏 canvas 中缓存不同图片的尺寸,不要使用 drawImage() 缩放
4,使用多层画布画复杂场景
如果遇到画布中有的元素一直不变,而其他元素不断的改变或移动,这种优化使用多个画布创建不同层次
比如 foreground 层处理一些经常变化的内容, background 层放置静态少动的背景
5,用 CSS transform 特性缩放画布
CSS transform 特性由于调用 GPU, 因此更快捷。最好情况是,不要把小画布放大,而是将达画布缩小。
let scaleX = canvas.width / window.innerWidth
let scaleY = canvas.height / window.innerHeight
let scaleToFit = Math.min(scaleX, scaleY)
let scaleToCover = Math.max(scale, scaleY)
state.style.transformOrigin = '0.0' // scale from top to left
state.style.tranform = `scale(${ scaleToFit })`
6,关闭透明度
如果场景不需要透明,把画布透明度设置为 false, 当使用 HTMLCanvasElement.getContext() 创建绘图上下文时候,把 alpha 选型设置为 false
const context = canvas.getContext('2d', {alpha: false})
如果使用 Firfox 浏览器可以添加 moz-opaque 优化渲染
<canvas id="mycanvas" moz-opaque></canvas>
7,将画布的函数调用集合到一起
由于绘图是昂贵的操作,高效的做法是使用一长串命令加载绘图状态机,然后把它转存到视频缓冲区
例如,画多条线时,创建一条包含所有线的路径,并通过一个画图调用进行绘制效率更高,不要单独画每一条线
// 画单独线
for (let i = 0; i < points.length - 1; i++) {
const p1 = points[i]
const p2 = points[i+1]
context.beginPath()
context.moveTo(p1.x, p1.y)
context.lineTo(p2.x, p2.y)
context.stroke()
}
// 使用一条路径包含所条线
context.beginPath()
for (let i = 0; i < points.length - 1; i++) {
const p1 = points[i]
const p2 = points[i+1]
context.moveTo(p1.x, p1.y)
context.lineTo(p2.x, p2.y)
}
特别注意
如果将要绘制的图元想要有一个最小的盒边界,单独渲染可能更好。例如画一条线之 closePath()
8,避免不必要的画布状态改变
canvas 元素在状态机的顶部实现,状态机根据填充、笔触样式之类的内容以及构成当前的路径的先前点。操作状态机也会导致性能开销
例如
// bad
for (var i = 0; i < STRIPES; i++) {
context.fillStyle = (i % 2 ? COLOR1 : COLOR2);
context.fillRect(i * GAP, 0, GAP, 480);
}
// better
context.fillStyle = COLOR1;
for (var i = 0; i < STRIPES/2; i++) {
context.fillRect((i*2) * GAP, 0, GAP, 480);
}
context.fillStyle = COLOR2;
for (var i = 0; i < STRIPES/2; i++) {
context.fillRect((i*2+1) * GAP, 0, GAP, 480);
}
9,渲染画布中的不同点, 而非整个新状态
少渲染比多渲染肯定节省性能,如果重绘只是增量差异,则只需要绘制差异可以获取性能提升
// bad
context.fillRect(0, 0, canvas.width, canvas.height)
// better
context.fillRect(last.x, last.y, canvas.width, canvas.height)
10,尽可能避免 shadowBlur text rendering
// 添加
context.shadowOffsetX = 5
context.shadowOffsetY = 5
context.shadowBlur = 4
context.shadowColor - 'rgba(255, 0, 0, 0.5)'
context.fillRect(20, 20, 150, 100)
(PS: 测试跑起来,电脑开始响起声音我就知道性能影响有多糟糕)
11,使用不同的办法清除画布(clearRect() vs fillRect()vs调整 canvas 大小 )
12,动画使用 window.requestAnimationFrame()
let x = 100
let y = 100
let lastREnder = Date.now()
function render() {
let delta = Date.now() - lastRender
x += delta
y += delta
context.fillRect(x, y, W, H)
requestAnimationFrame(render)
}
render()
13,谨慎使用大型物理库
14, 减少垃圾回收, 如果在切换帧之间频繁触发垃圾回收,会影响 FPS(frame per seconds)
减少对象创建, 可以使用一些公共对象池
一些内部方法会增加新对象 Array.slice Array.splice Function.bind
15,代码中微小细节调整
x | 0 代替 Math.floor()
// 清空数组
arr.length = 0 代替 arr = []
if () {} else {} 代替 switch
Date.now() > new Date().getTime() > +new Date()
推荐一些 canvas 相关的库
- EaselJS is an open-source canvas library that makes creating games, generative art, and other highly graphical experiences easy.
- Fabric.js is an open-source canvas library with SVG parsing capabilities.
- heatmap.js is an open-source library for creating canvas-based data heat maps.
- JavaScript InfoVis Toolkit creates interactive data visualizations.
- Konva.js is a 2D canvas library for desktop and mobile applications.
- p5.js has a full set of canvas drawing functionality for artists, designers, educators, and beginners.
- Paper.js is an open-source vector graphics scripting framework that runs on top of the HTML5 Canvas.
- Phaser is a fast, free and fun open source framework for Canvas and WebGL powered browser games.
- Processing.js is a port of the Processing visualization language.
- Pts.js is a library for creative coding and visualization in canvas and SVG.
- Rekapi is an animation key-framing API for Canvas.
- Scrawl-canvas is an open-source JavaScript library for creating and manipulating 2D canvas elements.
- The ZIM framework provides conveniences, components, and controls for coding creativity on the canvas — includes accessibility and hundreds of colorful tutorials.