概念
canvas是指令式绘图系统
svg是浏览器 DOM 来渲染的
性能
frame per second,一般要求是大于24fps
60fps:16ms完成计算与绘制
24fps:42ms完成计算与绘制
测试方法
工具:
chrome devTools
测试方法:
分别使用不同技术方案绘制圆形。
打开chrome devetools的rendering观察
canvas
影响canvas性能
1、绘制图形的数量
2、绘制图形的大小
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#canvas {
margin-top: 200px;
}
</style>
</head>
<body>
<canvas width="500" height="500" id="canvas"></canvas>
</body>
<script>
// 7
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const width = canvas.width
const height = canvas.height
function randomColor() { return `hsl(${Math.random() * 360}, 100%, 50%)`;}
function drawCircle(context, raduis) {
const x = Math.random() * width
const y = Math.random() * height
const fillColor = randomColor()
context.fillStyle = fillColor
context.beginPath()
context.arc(x, y, raduis, 0, Math.PI * 2)
context.fill()
}
function draw(context, count = 500, raduis=200) {
for(let i = 0; i < count; i++) {
drawCircle(context,raduis)
}
}
requestAnimationFrame(function update() {
ctx.clearRect(0,0,width,height)
draw(ctx)
requestAnimationFrame(update)
})
</script>
</html>
500个,半径10
3000个半径10
500个半径500.
影响因素
1、主要数量会影响svg的渲染帧率,数量大于3000明显低于canvas
2、大小对svg绘制帧率影响不大
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<svg></svg>
</body>
<script>
function randomColor() {
return `hsl(${Math.random() * 360}, 100%, 50%)`;
}
const root = document.querySelector('svg');
const COUNT = 3000;
const WIDTH = 500;
const HEIGHT = 500;
function initCircles(count = COUNT) {
for(let i = 0; i < count; i++) {
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
root.appendChild(circle);
}
return [...root.querySelectorAll('circle')];
}
const circles = initCircles();
function drawCircle(circle, radius = 10) {
const x = Math.random() * WIDTH;
const y = Math.random() * HEIGHT;
const fillColor = randomColor();
circle.setAttribute('cx', x);
circle.setAttribute('cy', y);
circle.setAttribute('r', radius);
circle.setAttribute('fill', fillColor);
}
function draw() {
for(let i = 0; i < COUNT; i++) {
drawCircle(circles[i]);
}
requestAnimationFrame(draw);
}
draw();
</script>
</html>
500个半径10
3000个半径10
500个半径500
一般来说,在图形数量小于 1000 时,我们可以考虑使用 SVG,当图形数量大于 1000 但不超过 3000 时,我们考虑使用 Canvas2D。(我的测试电脑是3000个为临界点,这个结论是月影老师给出的。)
WebGL影响因素
1、WebGL 和 Canvas2D 与 SVG 不同,它的性能并不直接与渲染元素的数量相关,而是取决于 WebGL 的渲染次数
2、元素的数量多,WebGL 渲染效率也会逐渐降低,这是因为,元素越多,本身渲染耗费的内存也越多,占用内存太多,渲染效率也会下降。
3、在渲染次数相同的情况下,WebGL 的效率取决于着色器中的计算复杂度和执行次数。图形顶点越多,顶点着色器的执行次数越多,图形越大,片元着色器的执行次数越多,虽然是并行执行,但执行次数多依然会有更大的性能开销。
4、最后,如果每次执行着色器中的计算越复杂,WebGL 渲染的性能开销自然也会越大。
总的来说,WebGL 的性能主要有三点决定因素,一是渲染次数,二是着色器执行的次数,三是着色器运算的复杂度。当然,数据的大小也会决定内存的消耗,因此也会对性能有所影响,只不过影响没有前面三点那么明显。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ogl Circles</title>
</head>
<body>
<canvas width="500" height="500"></canvas>
<script type="module">
import {Renderer, Program, Geometry, Transform, Mesh} from '../common/lib/ogl/index.mjs';
const canvas = document.querySelector('canvas');
const renderer = new Renderer({
canvas,
antialias: true,
width: 500,
height: 500,
});
const gl = renderer.gl;
gl.clearColor(1, 1, 1, 1);
function circleGeometry(gl, radius = 0.004, count = 30000, segments = 20) {
const tau = Math.PI * 2;
const position = new Float32Array(segments * 2 + 2);
const index = new Uint16Array(segments * 3);
const id = new Uint16Array(count);
for(let i = 0; i < segments; i++) {
const alpha = i / segments * tau;
position.set([radius * Math.cos(alpha), radius * Math.sin(alpha)], i * 2 + 2);
}
for(let i = 0; i < segments; i++) {
if(i === segments - 1) {
index.set([0, i + 1, 1], i * 3);
} else {
index.set([0, i + 1, i + 2], i * 3);
}
}
for(let i = 0; i < count; i++) {
id.set([i], i);
}
return new Geometry(gl, {
position: {
data: position,
size: 2,
},
index: {
data: index,
},
id: {
instanced: 1,
size: 1,
data: id,
},
});
}
// const geometry = new Box(gl);
const geometry = circleGeometry(gl);
const vertex = `
precision highp float;
attribute vec2 position;
attribute float id;
uniform float uTime;
highp float random(vec2 co) {
highp float a = 12.9898;
highp float b = 78.233;
highp float c = 43758.5453;
highp float dt= dot(co.xy ,vec2(a,b));
highp float sn= mod(dt,3.14);
return fract(sin(sn) * c);
}
// Function from Iñigo Quiles
// https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb(vec3 c){
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), 6.0)-3.0)-1.0, 0.0, 1.0);
rgb = rgb * rgb * (3.0 - 2.0 * rgb);
return c.z * mix(vec3(1.0), rgb, c.y);
}
varying vec3 vColor;
void main() {
vec2 offset = vec2(
1.0 - 2.0 * random(vec2(id + uTime, 100000.0)),
1.0 - 2.0 * random(vec2(id + uTime, 200000.0))
);
vec3 color = vec3(
random(vec2(id + uTime, 300000.0)),
1.0,
1.0
);
vColor = hsb2rgb(color);
gl_Position = vec4(position * 20.0 + offset, 0, 1);
}
`;
const fragment = `
precision highp float;
varying vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, 1);
}
`;
const program = new Program(gl, {
vertex,
fragment,
uniforms: {
uTime: {value: 0},
},
});
const scene = new Transform();
const mesh = new Mesh(gl, {geometry, program});
mesh.setParent(scene);
function update(t) {
program.uniforms.uTime.value = t / 1000;
renderer.render({scene});
requestAnimationFrame(update);
}
update(0);
</script>
</body>
</html>
30000个半径0.004(相当于cavans半径10)