前言
cache实例方法定义在Node基类上,通过该方法可以实现图形缓存,在Konva中Stage、Layer、Group、Shape等所有容器类和图形类都直接或间接继承了Node基类,故而都可以使用缓存方法。本篇文章就是探讨Konva背后的缓存机制,版本是v9.2.1。
Konva离屏缓存
就以下面的实例进行整体过程的分析:
const stage = new Konva.Stage({
container: 'root',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
const star = new Konva.Star({
innerRadius: 20,
outerRadius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 5,
numPoints: 5,
x: 60,
y: 60,
shadowOffset: { x: 5, y: 5 },
shadowColor: 'black',
shadowBlur: 5,
shadowOpacity: 0.5,
shadowForStrokeEnabled: false,
});
star.cache()
layer.add(star);
stage.add(layer);
// 创建10个Star
let clone;
for (var n = 0; n < 10; n++) {
clone = star.clone({
x: Math.random() * stage.width(),
y: Math.random() * stage.height(),
});
clone.cache();
layer.add(clone);
}
上面的实例就是创建11个Star图形,每个图形都会调用cache实例方法进行缓存。
cache处理流程
cache实例方法的处理逻辑如下:
当图形对象调用缓存方法cache时,其逻辑总结如下:
- 首先内部会计算当前图形的包围框信息(大小以及位置),通过包围框的大小信息创建cachedSceneCanvas、cachedFilterCanvas、cachedHitCanvas三个Canvas图层,这三个图层不会挂载在页面上仅仅位于内存中
- 然后将当前图形绘制到CachedSceneCanvas、CachedHitCanvas中
- 最后会将相关包围框位置信息以及CachedCanvas保存到_cache实例属性中,用于后续的逻辑处理
需要注意的是总会保存最新信息到_cache实例属性中,如果多次调用cache实例方法时会先删除_cache中存在的key,然后重新添加,逻辑如下:
cache() {
...
this._cache.delete(CANVAS);
...
this._cache.set(CANVAS, {
scene: cachedSceneCanvas,
ilter: cachedFilterCanvas,
hit: cachedHitCanvas,
x: x,
y: y,
});
...
缓存图形的渲染
Konva是批量渲染图形的,在之前
Konva批量渲染文章中就有较为详细的处理逻辑,缓存图形的渲染逻辑也包含在其中,只是之前并没有具体说明。实际上针对缓存图形的渲染处理具体逻辑如下:
_drawCachedSceneCanvas(context) {
context.save();
...
var cacheCanvas = this._getCachedSceneCanvas();
var ratio = cacheCanvas.pixelRatio;
context.drawImage(cacheCanvas._canvas, 0, 0, cacheCanvas.width / ratio, cacheCanvas.height / ratio);
context.restore();
}
drawScene(can, top) {
...
if (cachedSceneCanvas) {
context.save();
...
this._drawCachedSceneCanvas(context);
context.restore();
} else {
this._drawChildren('drawScene', canvas, top);
}
return this;
}
核心逻辑就是drawImage方法,对于缓存的图形实际上就是使用drawImage将其保存的CachedCanvas绘制到SceneCanvas中,而不是调用Canvas API进行具体的绘制。
总结
Konva缓存本质上就是创建位于内存中的Canvas图层,将当前图形绘制到CachedCanvas中,之后渲染时使用drawImage将整个CachedCanvas绘制到场景中,从而减少向CPU发送操作指令进而实现性能的提升。
从上面梳理逻辑知道每调用一次cache实例方法都会创建三个CachedCanvas并保存到对应属性中,如果图形很多,这是非常大的性能消耗,所以cache不能随便使用。实际上Konva官网也有cache的使用建议,可以去具体看看,这里就不再说明了。