HTML5 Canvas 禁用监听性能技巧
如果 Canvas 上有大量图形,并且部分图形不需要检测事件,
可以将 listening 设置为 false 以提高性能。
当 listening = false 时,事件检测会忽略该图形,例如 mouseover、拖放和 click 等事件。
这可以明显提高复杂应用的性能。
以下示例展示了启用监听和禁用监 听的图形之间的性能差异:
- Vanilla
- React
- Vue
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);
// Create many circles with listening enabled
for (let i = 0; i < 100; i++) {
const circle = new Konva.Circle({
x: Math.random() * stage.width(),
y: Math.random() * stage.height(),
radius: 20,
fill: 'blue',
opacity: 0.5,
// Enable event detection (default)
listening: true,
});
// Add hover effect
circle.on('mouseover', function() {
this.fill('red');
});
circle.on('mouseout', function() {
this.fill('blue');
});
layer.add(circle);
}
// Create many circles with listening disabled
for (let i = 0; i < 1000; i++) {
const circle = new Konva.Circle({
x: Math.random() * stage.width(),
y: Math.random() * stage.height(),
radius: 20,
fill: 'green',
opacity: 0.5,
// Disable event detection for better performance
listening: false,
});
layer.add(circle);
}
// Add text explanation
const text = new Konva.Text({
x: 10,
y: 10,
text: 'Blue circles (100) have event listeners (hover them)\nGreen circles (1000) have no listeners (better performance)',
fontSize: 16,
fill: 'black',
});
layer.add(text);
import { Stage, Layer, Circle, Text } from 'react-konva';
import { useState } from 'react';
// Generate circles data
const listeningCircles = Array.from({ length: 100 }, (_, i) => ({
id: i,
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
}));
const nonListeningCircles = Array.from({ length: 1000 }, (_, i) => ({
id: i + 100,
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
}));
const App = () => {
const [hoveredId, setHoveredId] = useState(null);
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
{/* Circles with event listeners */}
{listeningCircles.map((circle) => (
<Circle
key={circle.id}
x={circle.x}
y={circle.y}
radius={20}
fill={hoveredId === circle.id ? 'red' : 'blue'}
opacity={0.5}
onMouseEnter={() => setHoveredId(circle.id)}
onMouseLeave={() => setHoveredId(null)}
/>
))}
{/* Circles without event listeners */}
{nonListeningCircles.map((circle) => (
<Circle
key={circle.id}
x={circle.x}
y={circle.y}
radius={20}
fill="green"
opacity={0.5}
listening={false}
/>
))}
<Text
x={10}
y={10}
text={"Blue circles (100) have event listeners (hover them)\nGreen circles (1000) have no listeners (better performance)"}
fontSize={16}
fill="black"
/>
</Layer>
</Stage>
);
};
export default App;
<template>
<v-stage :config="stageSize">
<v-layer>
<!-- Circles with event listeners -->
<v-circle
v-for="circle in listeningCircles"
:key="circle.id"
:config="getListeningCircleConfig(circle)"
@mouseenter="handleMouseEnter(circle.id)"
@mouseleave="handleMouseLeave"
/>
<!-- Circles without event listeners -->
<v-circle
v-for="circle in nonListeningCircles"
:key="circle.id"
:config="getNonListeningCircleConfig(circle)"
/>
<v-text :config="textConfig" />
</v-layer>
</v-stage>
</template>
<script setup>
import { ref } from 'vue';
const stageSize = {
width: window.innerWidth,
height: window.innerHeight
};
const hoveredId = ref(null);
// Generate circles data
const listeningCircles = Array.from({ length: 100 }, (_, i) => ({
id: i,
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
}));
const nonListeningCircles = Array.from({ length: 1000 }, (_, i) => ({
id: i + 100,
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
}));
const getListeningCircleConfig = (circle) => ({
x: circle.x,
y: circle.y,
radius: 20,
fill: hoveredId.value === circle.id ? 'red' : 'blue',
opacity: 0.5,
});
const getNonListeningCircleConfig = (circle) => ({
x: circle.x,
y: circle.y,
radius: 20,
fill: 'green',
opacity: 0.5,
listening: false,
});
const textConfig = {
x: 10,
y: 10,
text: 'Blue circles (100) have event listeners (hover them)\nGreen circles (1000) have no listeners (better performance)',
fontSize: 16,
fill: 'black',
};
const handleMouseEnter = (id) => {
hoveredId.value = id;
};
const handleMouseLeave = () => {
hoveredId.value = null;
};
</script>