自动重绘 — 你需要 draw() 还是 batchDraw()?
简短回答:不需要。自 Konva 8 起,更改图形后无需调用 draw() 或 batchDraw()。
// This is enough. Konva schedules the redraw itself.
rect.fill('red');
rect.x(120);
// Both of these are redundant.
layer.draw();
layer.batchDraw();
设置任何属性都会将图层标记为脏,Konva 会在下一帧动画中重绘它。连续更改十个属性仍然只会产生一次重绘,因此自行调用 batchDraw() 没有任何作用——批处理已经完成。
你仍然会在较早的教程、2021 年以前编写的回答以及 AI 助手生成的代码中看到 layer.draw(),因为语料库中充斥着这类内容。它没有害处,只是不必要。删除这些调用不会改变屏幕上的显示效果。
仍然需要手动重绘的情况
自动绘制会响应 Konva 的更改。如果某些内容在 Konva 的认知范围之外发生变化,就不会有任何东西将图层标记为脏,你必须自行告知它。
常见情况是 Konva.Image 使用了会自行变化的源——例如一个 <video> 元素、一个动画 GIF,或者另一个库正在向其中绘制的原始 canvas:
// An external library drew a new GIF frame into our canvas.
// Konva has no attribute change to notice, so ask for the redraw.
function onDrawFrame(ctx, frame) {
ctx.drawImage(frame.buffer, 0, 0);
layer.draw();
}
对于视频,建议使用 Konva.Animation,它会为你重绘每一帧。在画布中播放视频和在画布中播放 GIF中提供了完整示例。
关闭批处理
Konva.autoDrawEnabled = false 会恢复旧行为,即在你发出请求之前不会进行任何绘制。只有当你自行驱动渲染循环,并且希望精确控制帧的绘制时机时,关闭它才值得。关闭后,batchDraw() 会再次变得有用:它会将同一帧中的多次调用合并为一次重绘,而不是每次调用都进行重绘。
下面的演示会关闭自动绘制,使 batchDraw() 有实际作用。矩形会在 mousemove 上旋转,而此事件的触发频率可能远高于屏幕刷新频率。
说明: 将鼠标移动到舞台上以旋转矩形
- Vanilla
- React
- Vue
import Konva from 'konva';
// Turned off on purpose, so batchDraw has a job to do
Konva.autoDrawEnabled = false;
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const rect = new Konva.Rect({
x: stage.width() / 2 - 50,
y: stage.height() / 2 - 25,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
});
layer.add(rect);
stage.on('mousemove', () => {
// 在鼠标移动时旋转矩形
rect.rotate(5);
// auto-draw is off, so ask for a batched redraw
layer.batchDraw();
});
import { Stage, Layer, Rect } from 'react-konva';
import { useRef, useEffect } from 'react';
// Turned off on purpose, so batchDraw has a job to do
Konva.autoDrawEnabled = false;
const App = () => {
const rectRef = useRef(null);
const layerRef = useRef(null);
useEffect(() => {
const stage = rectRef.current.getStage();
stage.on('mousemove', () => {
// 在鼠标移动时旋转矩形
rectRef.current.rotate(5);
// auto-draw is off, so ask for a batched redraw
layerRef.current.getLayer().batchDraw();
});
}, []);
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer ref={layerRef}>
<Rect
ref={rectRef}
x={window.innerWidth / 2 - 50}
y={window.innerHeight / 2 - 25}
width={100}
height={50}
fill="green"
stroke="black"
strokeWidth={4}
/>
</Layer>
</Stage>
);
};
export default App;
<template>
<v-stage :config="stageSize" @mousemove="handleMouseMove">
<v-layer ref="layerRef">
<v-rect :config="rectConfig" ref="rectRef" />
</v-layer>
</v-stage>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import Konva from 'konva';
// Turned off on purpose, so batchDraw has a job to do
Konva.autoDrawEnabled = false;
const stageSize = {
width: window.innerWidth,
height: window.innerHeight
};
const rectConfig = ref({
x: window.innerWidth / 2 - 50,
y: window.innerHeight / 2 - 25,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4
});
const layerRef = ref(null);
const rectRef = ref(null);
const handleMouseMove = () => {
// 在鼠标移动时旋转矩形
rectRef.value.getNode().rotate(5);
// auto-draw is off, so ask for a batched redraw
layerRef.value.getNode().batchDraw();
};
</script>