Drag and Drop Stress Test with 10,000 Shapes
This example demonstrates a stress test with 10,000 shapes. For simplicity, we're using just two layers - one main layer for all shapes and one dedicated drag layer. When we drag a shape, it's moved to the separate drag layer to ensure smooth movement.
- 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; // Move the circle back to the main layer target.moveTo(mainLayerRef.current); }; 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>