KonvaJS:如何实现多可拖拽图形的连线同步联动?
Hey there! Let's sort out that issue where your lines don't move along with the draggable boxes. The main problems in your current code are:
- You're creating duplicate boxes for child elements (since
boxesalready includes box2 and box3, but your loop draws them again when processing box_1's children) - Lines are only using the initial position of boxes—there's no logic to update their points when a box is dragged.
Here's a scalable solution that works for any number of boxes and connections:
Step 1: Refactor Your Data Structure
First, let's separate box data from their connections so we can track links clearly:
// Define your boxes with unique IDs let box_1 = {id: 'box1', x:20, y:20, width:150, height:100, color:'red'}; let box2 = {id: 'box2', x:500, y:20, width:150, height:100, color:'green'}; let box3 = {id: 'box3', x:300, y:300, width:150, height:100, color:'blue'}; let boxes = [box_1, box2, box3]; // Track connections separately (each entry links two box IDs) let connections = [ {from: 'box1', to: 'box2'}, {from: 'box1', to: 'box3'} ];
Step 2: Draw Boxes (No Duplicates!)
We'll draw each box once, and store a reference to the Konva Rect object in our box data for easy access later:
function drawBoxes(listOfBoxes) { for (let boxData of listOfBoxes) { const boxShape = new Konva.Rect({ id: boxData.id, x: boxData.x, y: boxData.y, width: boxData.width, height: boxData.height, fill: boxData.color, stroke: 'black', strokeWidth: 4, draggable: true }); // Save the Konva shape reference boxData.shape = boxShape; layer.add(boxShape); } }
Step 3: Draw Lines with Drag Sync Logic
We need two helper functions: one to fetch a box by ID, and another to update a line's position based on its connected boxes. We'll bind the update function to the dragmove event of each box so lines move in real-time.
// Helper to get a box by its ID function getBoxById(id) { return boxes.find(box => box.id === id); } // Update line points to match current box positions function updateLine(line, fromBox, toBox) { // Optional: connect centers of boxes (adjust to edges if needed) const fromCenterX = fromBox.shape.getX() + fromBox.width / 2; const fromCenterY = fromBox.shape.getY() + fromBox.height / 2; const toCenterX = toBox.shape.getX() + toBox.width / 2; const toCenterY = toBox.shape.getY() + toBox.height / 2; line.points([fromCenterX, fromCenterY, toCenterX, toCenterY]); layer.draw(); // Refresh the layer to show changes } // Draw all connections and bind drag events function drawConnections(connectionList) { for (let conn of connectionList) { const fromBox = getBoxById(conn.from); const toBox = getBoxById(conn.to); const line = new Konva.Line({ stroke: 'black', strokeWidth: 5, lineCap: 'round', lineJoin: 'round' }); // Store references to connected boxes in the line line.fromBox = fromBox; line.toBox = toBox; // Set initial line position updateLine(line, fromBox, toBox); layer.add(line); // Update line when either connected box is dragged fromBox.shape.on('dragmove', () => updateLine(line, fromBox, toBox)); toBox.shape.on('dragmove', () => updateLine(line, fromBox, toBox)); } }
Step 4: Assemble Everything
Put it all together with your stage setup:
var stage = new Konva.Stage({ container: 'container', width: 600, height: 800 }); var layer = new Konva.Layer(); drawBoxes(boxes); drawConnections(connections); stage.add(layer);
What This Fixes:
- No More Duplicate Boxes: Each box is drawn exactly once from the
boxesarray. - Real-Time Line Updates: Every time you drag a box, all lines connected to it update their position instantly.
- Scalable: Add more boxes and connections just by adding entries to the
boxesandconnectionsarrays—no need to rewrite core logic. - Adjustable Line Placement: If you prefer lines to connect edges instead of centers, modify the
updateLinefunction (e.g., usefromBox.shape.getX() + fromBox.widthfor the right edge of the source box).
内容的提问来源于stack exchange,提问作者azlan_909

