10,000 个图形拖放压力测试
此示例演示包含 10,000 个图形的压力测试。为简化示例,只使用两个图层:一个用于所有图形的主图层,以及一个专用拖动图层。拖动图形时,示例会将其移动到单独的拖动图层,以确保移动流畅。
- Vanilla
- React
- Vue
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
// Create stage
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
// Create main layer for all shapes
const mainLayer = new Konva.Layer();
// Create a dedicated layer for dragging
const dragLayer = new Konva.Layer();
// Define colors for random shapes
const colors = [
'red',
'orange',
'yellow',
'green',
'blue',
'cyan',
'purple',
];
let colorIndex = 0;
// Helper function to add a circle to a layer
function addCircle(layer) {
const color = colors[colorIndex++];
if (colorIndex >= colors.length) {
colorIndex = 0;
}
const randX = Math.random() * stage.width();
const randY = Math.random() * stage.height();
const circle = new Konva.Circle({
x: randX,
y: randY,
radius: 6,
fill: color,
});
layer.add(circle);
}
// Create 10,000 circles on the main layer
for (let n = 0; n < 10000; n++) {
addCircle(mainLayer);
}
// Add the main layer and drag layer to the stage
stage.add(mainLayer);
stage.add(dragLayer);
// Setup drag and drop behavior
stage.on('mousedown', function (evt) {
const circle = evt.target;
// Only handle circle shapes (ignore clicks on empty space)
if (!circle || circle.getClassName() !== 'Circle') {
return;
}
// Move the circle to the drag layer
circle.moveTo(dragLayer);
circle.startDrag();
});
// When dragging stops, move the circle back to the main layer
stage.on('mouseup', function (evt) {
const circle = evt.target;
// Only handle circle shapes
if (!circle || circle.getClassName() !== 'Circle') {
return;
}
// Move the circle back to the main layer
circle.moveTo(mainLayer);
});
import { useState, useEffect, useRef } from 'react';
import { Stage, Layer, Circle } from 'react-konva';
const COLORS = ['red', 'orange', 'yellow', 'green', 'blue', 'cyan', 'purple'];
const SHAPE_COUNT = 10000;
const App = () => {
// State to hold all the circles data
const [circles, setCircles] = useState([]);
// Refs to layers
const mainLayerRef = useRef(null);
const dragLayerRef = useRef(null);
// Initialize circles data
useEffect(() => {
const circlesData = [];
// Create 10,000 circles
for (let i = 0; i < SHAPE_COUNT; i++) {
circlesData.push({
id: i,
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
radius: 6,
fill: COLORS[i % COLORS.length]
});
}
setCircles(circlesData);
}, []);
// This is not the typical "React way" of managing components.
// In a more React-friendly approach, we would update state and let React handle the DOM.
// However, for this performance demo, we're directly manipulating the nodes
// to match the vanilla JS implementation.
const handleDragStart = (e) => {
const target = e.target;
// Move the circle to the drag layer
target.moveTo(dragLayerRef.current);
};
const handleDragEnd = (e) => {
const target = e.target;
const id = target.id();
const { x, y } = target.position();
// Move the circle back to the main layer
target.moveTo(mainLayerRef.current);
// Store the final position without re-rendering 10,000 nodes on each move.
setCircles(currentCircles => currentCircles.map(circle =>
String(circle.id) === String(id) ? { ...circle, x, y } : circle
));
};
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
{/* Main layer for all circles */}
<Layer ref={mainLayerRef}>
{circles.map(circle => (
<Circle
key={circle.id}
id={circle.id}
x={circle.x}
y={circle.y}
radius={circle.radius}
fill={circle.fill}
draggable
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
/>
))}
</Layer>
{/* Empty drag layer that will receive circles during drag */}
<Layer ref={dragLayerRef} />
</Stage>
);
};
export default App;
<template>
<v-stage :config="stageConfig">
<!-- Main layer for all circles -->
<v-layer ref="mainLayer">
<v-circle
v-for="circle in circles"
:key="circle.id"
:config="{
id: circle.id,
x: circle.x,
y: circle.y,
radius: 6,
fill: circle.fill,
draggable: true
}"
@dragstart="handleDragStart"
@dragend="handleDragEnd"
/>
</v-layer>
<!-- Empty drag layer that will receive circles during drag -->
<v-layer ref="dragLayer" />
</v-stage>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const COLORS = ['red', 'orange', 'yellow', 'green', 'blue', 'cyan', 'purple'];
const SHAPE_COUNT = 10000;
const stageConfig = {
width: window.innerWidth,
height: window.innerHeight
};
// Refs for layers
const mainLayer = ref(null);
const dragLayer = ref(null);
// State for circles
const circles = ref([]);
// Initialize circles data
onMounted(() => {
const circlesData = [];
// Create 10,000 circles
for (let i = 0; i < SHAPE_COUNT; i++) {
circlesData.push({
id: i,
x: Math.random() * stageConfig.width,
y: Math.random() * stageConfig.height,
radius: 6,
fill: COLORS[i % COLORS.length]
});
}
circles.value = circlesData;
});
// This is not the typical "Vue way" of managing components.
// In a more Vue-friendly approach, we would update state and let Vue handle the DOM.
// However, for this performance demo, we're directly manipulating the nodes
// to match the vanilla JS implementation.
const handleDragStart = (e) => {
const target = e.target;
// Move the circle to the drag layer
target.moveTo(dragLayer.value.getNode());
};
const handleDragEnd = (e) => {
const target = e.target;
// Move the circle back to the main layer
target.moveTo(mainLayer.value.getNode());
};
</script>