为许多图像优化 Konva.js
Optimizing Konva.js for Many Images
我目前正在使用 Konva.js 在几个堆叠的 FastLayers 上平铺许多 PNG 图像。 PNG 包含不透明度,并且不需要拖动或点击框。瓷砖经常更换,这似乎适用于尺寸约为 30x30 的中型网格。一旦图块开始增长到大约 100x100,甚至 60x60,替换单个图块时性能开始下降。
我已经开始研究 "chunking" tiles,即,将 tiles 添加到较小的 FastLayer 组中。例如,一个 100x100 的 FastLayer 将被分成几个 10x10 的 FastLayer。当单个图块发生变化时,我们的想法是只有那个块应该重新渲染,理想情况下可以加快整体渲染时间。
这是一个值得尝试的好设计,还是我应该尝试不同的方法?我查看了 Konva.js 文档中的性能提示,但没有看到任何与此案例直接相关的内容。
因此,经过一些研究和修改,我发现了渲染约 4000 张图像的最快方法。
- 不要为 Konva.js 使用 React 组件。我使用 React 构建我的应用程序,但我跳过了使用中间库进行 Konva.js 渲染。 canvas 使用 React Components 会使你的性能减半。
- 缓存常用图片。我使用简单的 LRU 缓存来重用 HTMLImageElement 对象。
- 尽可能重复使用 Konva.js 个节点 (Konva.Image)。我的实现是渲染图像网格。位置不会改变,但图像可能会改变。之前,我会 destroy() 一个节点,然后添加另一个节点。 destroy() 会导致额外的渲染,这会为您的用户造成卡顿。相反,我只是将 image() 方法与 id() 和 name() 结合使用,以在网格坐标处查找和替换图像。
- 我的应用程序允许用户在网格上绘制长笔画。当仅使用文字鼠标事件时,这在小笔画中工作正常。对于长行程,这不起作用有两个原因。首先,OS 和浏览器会限制鼠标事件,使您出现间歇性的鼠标事件。其次,处于渲染中间会产生相同的副作用。取而代之的是,该软件现在会检测长笔划,以及 "fills in" 用户打算在间歇性鼠标事件之间绘制的缺失坐标。
- 每隔一段时间渲染一次。由于我的网格经常变化,我决定每秒对网格信息进行 24 次采样,而不是让每个图块变化都排队 batchDraw()。底层实现是使用 RxJS 每 42 毫秒轮询一次 Redux 存储,并且仅在发生更改时才对 batchDraw() 进行排队。
缓存确实有助于提高性能,但隐藏也同样如此。 Konva 没有(或者我最后没有研究过这个)做任何视图剔除。下面是我用来在我的 Konva 策略游戏中隐藏岛屿形状的代码。
stage.on('dragmove', function() {
cullView();
});
function cullView() {
var boundingX = ((-1 * (stage.x() * (1/zoomLevel)))-(window.innerWidth/2))-degreePixels;
var boundingY = ((-1 * (stage.y() * (1/zoomLevel)))-(window.innerHeight/2))-degreePixels;
var boundingWidth = (2 * window.innerWidth * (1/zoomLevel)) + (2*degreePixels);
var boundingHeight = (2 * window.innerHeight * (1/zoomLevel)) + (2*degreePixels);
var x = 0;
var y = 0;
for (var i = 0; i < oceanIslands.length; i++) {
x = oceanIslands[i].getX();
y = oceanIslands[i].getY();
if (((x > boundingX) && (x < (boundingX + boundingWidth))) && ((y > boundingY) && (y < (boundingY + boundingHeight)))) {
if (!oceanIslands[i].visible()) {
oceanIslands[i].show();
oceanIslands[i].clearCache();
if (zoomLevel <= cacheMaxZoom) {
oceanIslands[i].cache();
}
}
} else {
oceanIslands[i].hide();
}
}
我目前正在使用 Konva.js 在几个堆叠的 FastLayers 上平铺许多 PNG 图像。 PNG 包含不透明度,并且不需要拖动或点击框。瓷砖经常更换,这似乎适用于尺寸约为 30x30 的中型网格。一旦图块开始增长到大约 100x100,甚至 60x60,替换单个图块时性能开始下降。
我已经开始研究 "chunking" tiles,即,将 tiles 添加到较小的 FastLayer 组中。例如,一个 100x100 的 FastLayer 将被分成几个 10x10 的 FastLayer。当单个图块发生变化时,我们的想法是只有那个块应该重新渲染,理想情况下可以加快整体渲染时间。
这是一个值得尝试的好设计,还是我应该尝试不同的方法?我查看了 Konva.js 文档中的性能提示,但没有看到任何与此案例直接相关的内容。
因此,经过一些研究和修改,我发现了渲染约 4000 张图像的最快方法。
- 不要为 Konva.js 使用 React 组件。我使用 React 构建我的应用程序,但我跳过了使用中间库进行 Konva.js 渲染。 canvas 使用 React Components 会使你的性能减半。
- 缓存常用图片。我使用简单的 LRU 缓存来重用 HTMLImageElement 对象。
- 尽可能重复使用 Konva.js 个节点 (Konva.Image)。我的实现是渲染图像网格。位置不会改变,但图像可能会改变。之前,我会 destroy() 一个节点,然后添加另一个节点。 destroy() 会导致额外的渲染,这会为您的用户造成卡顿。相反,我只是将 image() 方法与 id() 和 name() 结合使用,以在网格坐标处查找和替换图像。
- 我的应用程序允许用户在网格上绘制长笔画。当仅使用文字鼠标事件时,这在小笔画中工作正常。对于长行程,这不起作用有两个原因。首先,OS 和浏览器会限制鼠标事件,使您出现间歇性的鼠标事件。其次,处于渲染中间会产生相同的副作用。取而代之的是,该软件现在会检测长笔划,以及 "fills in" 用户打算在间歇性鼠标事件之间绘制的缺失坐标。
- 每隔一段时间渲染一次。由于我的网格经常变化,我决定每秒对网格信息进行 24 次采样,而不是让每个图块变化都排队 batchDraw()。底层实现是使用 RxJS 每 42 毫秒轮询一次 Redux 存储,并且仅在发生更改时才对 batchDraw() 进行排队。
缓存确实有助于提高性能,但隐藏也同样如此。 Konva 没有(或者我最后没有研究过这个)做任何视图剔除。下面是我用来在我的 Konva 策略游戏中隐藏岛屿形状的代码。
stage.on('dragmove', function() {
cullView();
});
function cullView() {
var boundingX = ((-1 * (stage.x() * (1/zoomLevel)))-(window.innerWidth/2))-degreePixels;
var boundingY = ((-1 * (stage.y() * (1/zoomLevel)))-(window.innerHeight/2))-degreePixels;
var boundingWidth = (2 * window.innerWidth * (1/zoomLevel)) + (2*degreePixels);
var boundingHeight = (2 * window.innerHeight * (1/zoomLevel)) + (2*degreePixels);
var x = 0;
var y = 0;
for (var i = 0; i < oceanIslands.length; i++) {
x = oceanIslands[i].getX();
y = oceanIslands[i].getY();
if (((x > boundingX) && (x < (boundingX + boundingWidth))) && ((y > boundingY) && (y < (boundingY + boundingHeight)))) {
if (!oceanIslands[i].visible()) {
oceanIslands[i].show();
oceanIslands[i].clearCache();
if (zoomLevel <= cacheMaxZoom) {
oceanIslands[i].cache();
}
}
} else {
oceanIslands[i].hide();
}
}