Skip to main content

HTML5 Canvas 形状缓存性能提示

如果您有一个复杂的形状,其中包含许多绘图操作,或者如果您正在应用过滤器,您可以通过缓存形状来提高性能。当您缓存一个形状时,Konva 会将其绘制到一个内部画布缓冲区。之后,Konva 将简单地使用缓存的版本,而不是每次都重绘该形状。

这在以下情况下特别有用:

  1. 具有许多绘图操作的复杂形状
  2. 带有过滤器的形状
  3. 不经常变化但需要频繁重新绘制的形状

要缓存形状,只需调用 cache() 方法。您可以使用 clearCache() 清除缓存。

缓存是如何工作的?

当您在形状上调用 cache() 方法时,Konva:

  1. 创建一个内部画布缓冲区
  2. 将形状绘制到该缓冲区
  3. 将该缓冲区存储以供将来使用

在缓存之后,而不是每次需要显示该形状时都进行重绘,Konva 将简单地使用缓冲区中的缓存版本。这比重复重绘形状要快得多。

指南

  1. 不要缓存没有过滤器的简单形状。直接渲染它可能会更快,而不是从缓存版本中渲染。
  2. 每个缓存节点会创建几个画布缓冲区。因此不要过度使用,因为这会消耗大量内存。
  3. 更好地缓存形状组,而不是单独缓存每个形状。
  4. 记得始终测量带缓存和不带缓存的性能,以查看实际差异。

下面是一个演示,显示了缓存和非缓存复杂形状之间的性能差异:

说明:

  • 点击舞台上的任何地方添加 1000 个更多的圆圈
  • 切换复选框以启用/禁用缓存
  • 观察 FPS 计数器以查看性能差异
  • 圆圈组不断旋转
import Konva from 'konva';

const stage = new Konva.Stage({
  container: 'container',
  width: window.innerWidth,
  height: window.innerHeight,
});

const layer = new Konva.Layer();
stage.add(layer);

// 创建一组圆圈
const group = new Konva.Group({
  x: stage.width() / 2,
  y: stage.height() / 2,
});
layer.add(group);

// 添加初始圆圈
const addCircles = (count) => {
  const radius = 300;
  for (let i = 0; i < count; i++) {
    const angle = Math.random() * Math.PI * 2;
    const distance = Math.random() * radius;
    const x = Math.cos(angle) * distance;
    const y = Math.sin(angle) * distance;

    const circle = new Konva.Circle({
      x,
      y,
      radius: 5 + Math.random() * 10,
      fill: Konva.Util.getRandomColor(),
      shadowColor: 'black',
      shadowBlur: 10,
      shadowOpacity: 0.5,
      shadowOffset: { x: 2, y: 2 },
      listening: false,
    });

    group.add(circle);
  }
};

// 添加初始圆圈
addCircles(5000);

// 添加 FPS 计数器
const fpsText = new Konva.Text({
  x: 10,
  y: 10,
  text: 'FPS: 0',
  fontSize: 16,
  fill: 'white',
  shadowColor: 'black',
  shadowBlur: 5,
  shadowOffset: { x: 1, y: 1 }
});
layer.add(fpsText);

// 添加圆圈数量文本
const countText = new Konva.Text({
  x: 10,
  y: 40,
  text: 'Circles: 1000',
  fontSize: 16,
  fill: 'white',
  shadowColor: 'black',
  shadowBlur: 5,
  shadowOffset: { x: 1, y: 1 }
});
layer.add(countText);

// 创建动画
const anim = new Konva.Animation((frame) => {
  group.rotation(frame.time * 0.05);
  
  // 更新 FPS 计数器
  fpsText.text('FPS: ' + frame.frameRate.toFixed(1));
}, layer);

// 添加点击处理程序以添加更多圆圈
stage.on('click', () => {
  addCircles(1000);
  countText.text('Circles: ' + group.children.length);
});

// 添加 DOM 复选框
const container = stage.container();
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = 'cache-toggle';
checkbox.style.position = 'absolute';
checkbox.style.top = '70px';
checkbox.style.left = '10px';
checkbox.style.zIndex = '100';
container.appendChild(checkbox);

const label = document.createElement('label');
label.htmlFor = 'cache-toggle';
label.textContent = '启用缓存';
label.style.position = 'absolute';
label.style.top = '70px';
label.style.left = '30px';
label.style.color = 'white';
label.style.textShadow = '0 0 5px black';
label.style.zIndex = '100';
container.appendChild(label);

// 切换缓存
checkbox.addEventListener('change', () => {
  if (checkbox.checked) {
    group.cache();
  } else {
    group.clearCache();
  }
});

anim.start();