Open chokcoco opened 6 years ago
最近业务上需要制作一个星云图来展示一些实时数据,像这样: 涉及 SVG 绘图、Canvas 等技术,其中使用 Canvas 瞬时渲染百万级别的粒子及动态去改变这些粒子是整个项目的核心所在。
所以,如何优化海量粒子的瞬时渲染及动态改变这些粒子就非常非常的关键。
工欲善其事必先利其器,我们先来看看对于这样一个多粒子渲染的页面,性能都消耗在了什么地方。
我们常说一个动画很卡,也就是说这个动画的帧率较低。流畅动画的标准一般是 60 FPS。Canvas 渲染动画的基本原理,本质就是不断地重绘画布。
把动画的一帧渲染出来,需要经过以下步骤:
由 Javascript 计算过程产生的卡顿,一般是一次性发生的。包括了业务逻辑、坐标计算、对象状态等等。
一般称之为掉帧,它是周期性发生的。渲染过程本质上也有两个过程。
所以要优化渲染造成的卡顿,总体思路很简单,归纳为以下几点(Canvas 最佳实践(性能篇)):
计算的卡顿一般出现在某一帧突然需要绘制非常多屏幕内不存在的动画内容,这时在这一帧中造成过大计算量,导致整一帧的时间超过超过16.67ms,阻塞了后续 requestAnimationFrame 的执行,这就会造成一次性的一次卡顿。
另一种情况是每一帧的计算量都稍稍偏多,导致了每一帧的时间都小幅度超过了 16.67ms,但是总体不会造成特别大的一次性卡顿。
针对上面两种情况,也就是说,我们需要解决两种阻塞:
现在主流的解决计算卡顿的方法有两个。
对于我这个项目,瞬时绘制百万级别的粒子,粒子绘制不存在一些算法和 DOM 操作,对于优化计算卡顿的需求迫切度不高,主要需要进行渲染优化。
和真正的绘制相比,计算所产生的开销是其实微不足道的。
Canvas API 都在其上下文对象 context 上调用。而每次调用 context 相关的 API,都是对性能的一次消耗。
改变 context 的状态,几乎都与最终的渲染操作有关。譬如我们需要在画布的(100px, 100px)处绘制一个1px半径的粒子:
var context = canvasElement.getContext('2d'); context .fillStyle = rgba(35, 117, 204, .8); context .beginPath(); context .arc(100, 100, 1, 0, 2 * Math.PI, true); context .fill();
当我们对 context.fillStyle 赋值,浏览器会需要立刻地做一些事情,这样当我们下次调用 context .fill() 时,保证填充进去的颜色是我们设定的。
context.fillStyle
context .fill()
------------ 未完成。。。
后续呢。。
后续呢?
这是来自QQ邮箱的假期自动回复邮件。你好,邮件已收到,尽快给你回复。
最近业务上需要制作一个星云图来展示一些实时数据,像这样: 涉及 SVG 绘图、Canvas 等技术,其中使用 Canvas 瞬时渲染百万级别的粒子及动态去改变这些粒子是整个项目的核心所在。
所以,如何优化海量粒子的瞬时渲染及动态改变这些粒子就非常非常的关键。
工欲善其事必先利其器,我们先来看看对于这样一个多粒子渲染的页面,性能都消耗在了什么地方。
Canvas 卡顿原因
我们常说一个动画很卡,也就是说这个动画的帧率较低。流畅动画的标准一般是 60 FPS。Canvas 渲染动画的基本原理,本质就是不断地重绘画布。
把动画的一帧渲染出来,需要经过以下步骤:
计算卡顿
由 Javascript 计算过程产生的卡顿,一般是一次性发生的。包括了业务逻辑、坐标计算、对象状态等等。
渲染卡顿
一般称之为掉帧,它是周期性发生的。渲染过程本质上也有两个过程。
所以要优化渲染造成的卡顿,总体思路很简单,归纳为以下几点(Canvas 最佳实践(性能篇)):
解决计算卡顿
计算的卡顿一般出现在某一帧突然需要绘制非常多屏幕内不存在的动画内容,这时在这一帧中造成过大计算量,导致整一帧的时间超过超过16.67ms,阻塞了后续 requestAnimationFrame 的执行,这就会造成一次性的一次卡顿。
另一种情况是每一帧的计算量都稍稍偏多,导致了每一帧的时间都小幅度超过了 16.67ms,但是总体不会造成特别大的一次性卡顿。
针对上面两种情况,也就是说,我们需要解决两种阻塞:
现在主流的解决计算卡顿的方法有两个。
对于我这个项目,瞬时绘制百万级别的粒子,粒子绘制不存在一些算法和 DOM 操作,对于优化计算卡顿的需求迫切度不高,主要需要进行渲染优化。
解决渲染优化
和真正的绘制相比,计算所产生的开销是其实微不足道的。
减少 Canvas API 调用次数
Canvas API 都在其上下文对象 context 上调用。而每次调用 context 相关的 API,都是对性能的一次消耗。
改变 context 的状态,几乎都与最终的渲染操作有关。譬如我们需要在画布的(100px, 100px)处绘制一个1px半径的粒子:
当我们对
context.fillStyle
赋值,浏览器会需要立刻地做一些事情,这样当我们下次调用context .fill()
时,保证填充进去的颜色是我们设定的。------------ 未完成。。。