Skip to content

canvas性能优化 #36

@beiweiqiang

Description

@beiweiqiang

canvas性能优化

欣(bei)然(bi)接受师父要求,做一个canvas优化的个人见解的小分享
(认识较浅,只能聊一点点,装不了比)

春招选了萝卜大战洞穴的题目,在动画展示上用到了canvas,做完以后在pc端运行当然很流畅,但是一到移动端实验就呵呵excuse me了。动画各种卡,各种掉帧。
然后就必须要进行canvas优化了,师父给我这个网站,以下是见解


1. 尽量少调用canvasAPI

用一种很low的方式来测试canvas性能

优化前

var start_time = (new Date()).getTime();
var canvas = document.getElementById("canvas");
canvas.width = 400;
canvas.height = 400;
var ctx = canvas.getContext("2d");
for (var i = 0; i < 400; i++) {
  for (var j = 0; j < 400; j++) {
    ctx.beginPath();
    ctx.moveTo(i, j);
    ctx.lineTo(i, j+1);
    ctx.stroke();  
  }
} 
var end_time = (new Date()).getTime();
console.log(end_time - start_time); // 平均330+

优化后

var start_time = (new Date()).getTime();
var canvas = document.getElementById("canvas");
canvas.width = 400;
canvas.height = 400;
var ctx = canvas.getContext("2d");
ctx.beginPath();
for (var i = 0; i < 400; i++) {
  for (var j = 0; j < 400; j++) {
    ctx.moveTo(i, j);
    ctx.lineTo(i, j+1);
  }
} 
ctx.stroke();  
var end_time = (new Date()).getTime();
console.log(end_time - start_time); // 平均32+

对这个我有很深的体会,因为那个任务里我主要用了这个优化方法。
比如在那个游戏里面,我渲染了上下两个边界,加之因为是像素级别的操作,所以调用api次数非常多。
换个角度思考,只渲染两个边界的中间部分,调用api的次数变为原来的一半。

2. 尽量少改变canvas状态

优化前

var start_time = (new Date()).getTime();
var canvas = document.getElementById("canvas");
canvas.width = 1000;
canvas.height = 1000;
var ctx = canvas.getContext("2d");
for (var i = 0; i < 500 ; i++) {
  for (var j = 0; j < 500 ; j++) {
    ctx.fillStyle = (j % 2 ? '#ff5252' : '#1976d2');
    ctx.fillRect(j *  2, i * 2, 2, 2);
  }
} 
var end_time = (new Date()).getTime();
console.log(end_time - start_time); // 平均220+

优化后

var start_time = (new Date()).getTime();
var canvas = document.getElementById("canvas");
canvas.width = 1000;
canvas.height = 1000;
var ctx = canvas.getContext("2d");
ctx.fillStyle = '#ff5252';
for (var i = 0; i < 500; i++) {
  for (var j = 0; j < 500 / 2; j++) {
    ctx.fillRect((j * 2) * 2, i * 2, 2, 2);
  }
}
ctx.fillStyle = '#1976d2';
for (var i = 0; i < 500; i++) {
  for (var j = 0; j < 500 / 2; j++) {
    ctx.fillRect((j * 2 + 1) * 2, i * 2, 2, 2);
  }
}
var end_time = (new Date()).getTime();
console.log(end_time - start_time); // 平均120+

3. 重新渲染的范围尽量小

这个当然会优化,所以就不举例了~

4. 不要使用阴影

优化前

var start_time = (new Date()).getTime();
var canvas = document.getElementById("canvas");
canvas.width = 1000;
canvas.height = 1000;
var ctx = canvas.getContext("2d");
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 2;
ctx.shadowColor = 'rgba(255, 0, 0, 0.5)';
for (var i = 0; i < 300 ; i++) {
  for (var j = 0; j < 300 ; j++) {
    ctx.fillRect(j *  2, i * 2, 2, 2);
  }
} 
var end_time = (new Date()).getTime();
console.log(end_time - start_time); // 平均2800+

优化后

var start_time = (new Date()).getTime();
var canvas = document.getElementById("canvas");
canvas.width = 1000;
canvas.height = 1000;
var ctx = canvas.getContext("2d");
for (var i = 0; i < 300 ; i++) {
  for (var j = 0; j < 300 ; j++) {
    ctx.fillRect(j *  2, i * 2, 2, 2);
  }
} 
var end_time = (new Date()).getTime();
console.log(end_time - start_time); // 平均40+

5. 像素级别操作尽量用整数

优化前

var start_time = (new Date()).getTime();
var canvas = document.getElementById("canvas");
canvas.width = 1000;
canvas.height = 1000;
var ctx = canvas.getContext("2d");
for (var i = 0; i < 1000 ; i++) {
  for (var j = 0; j < 1000 ; j++) {
    ctx.fillRect(j *  0.99, i * 0.99, 0.99, 0.99);
  }
} 
var end_time = (new Date()).getTime();
console.log(end_time - start_time); // 平均1200+

意外发现这个渲染出来是这样子
share01

优化后

var start_time = (new Date()).getTime();
var canvas = document.getElementById("canvas");
canvas.width = 1000;
canvas.height = 1000;
var ctx = canvas.getContext("2d");
for (var i = 0; i < 1000 ; i++) {
  for (var j = 0; j < 1000 ; j++) {
    ctx.fillRect(j *  1, i * 1, 1, 1);
  }
} 
var end_time = (new Date()).getTime();
console.log(end_time - start_time); // 平均440+

6. 总结

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions