如何使用 React 和 Konva 调整画布形状的大小和旋转?
目前还没有一个好用的、纯声明式的“React 方式”来使用 Transformer 工具。 但你依然可以通过对 Konva 节点进行一些小的手动操作来使用它。 它依旧可以正常工作。
思路是:你需要创建一个 Konva.Transformer
节点,并手动将其附加到所需节点上。
使用说明:点击其中一个矩形以选择它。然后你可以:
- 拖动它移动
- 使用控制点调整大小
- 点击画布空白处取消选择
import React from 'react'; import { Stage, Layer, Rect, Transformer } from 'react-konva'; const Rectangle = ({ shapeProps, isSelected, onSelect, onChange }) => { const shapeRef = React.useRef(); const trRef = React.useRef(); React.useEffect(() => { if (isSelected) { // 需要手动附加转换器 trRef.current.nodes([shapeRef.current]); } }, [isSelected]); return ( <React.Fragment> <Rect onClick={onSelect} onTap={onSelect} ref={shapeRef} {...shapeProps} draggable onDragEnd={(e) => { onChange({ ...shapeProps, x: e.target.x(), y: e.target.y(), }); }} onTransformEnd={(e) => { // 转换器改变节点的缩放比例 // 而非直接改变宽度或高度 // 但存储的数据里只有宽度和高度 // 为了数据一致性,转换结束时重置缩放比例 const node = shapeRef.current; const scaleX = node.scaleX(); const scaleY = node.scaleY(); // 重置缩放比例 node.scaleX(1); node.scaleY(1); onChange({ ...shapeProps, x: node.x(), y: node.y(), // 设定最小值 width: Math.max(5, node.width() * scaleX), height: Math.max(node.height() * scaleY), }); }} /> {isSelected && ( <Transformer ref={trRef} flipEnabled={false} boundBoxFunc={(oldBox, newBox) => { // 限制调整大小的最小边界 if (Math.abs(newBox.width) < 5 || Math.abs(newBox.height) < 5) { return oldBox; } return newBox; }} /> )} </React.Fragment> ); }; const initialRectangles = [ { x: 10, y: 10, width: 100, height: 100, fill: 'red', id: 'rect1', }, { x: 150, y: 150, width: 100, height: 100, fill: 'green', id: 'rect2', }, ]; const App = () => { const [rectangles, setRectangles] = React.useState(initialRectangles); const [selectedId, selectShape] = React.useState(null); const checkDeselect = (e) => { // 点击空白区域时取消选择 const clickedOnEmpty = e.target === e.target.getStage(); if (clickedOnEmpty) { selectShape(null); } }; return ( <Stage width={window.innerWidth} height={window.innerHeight} onMouseDown={checkDeselect} onTouchStart={checkDeselect} > <Layer> {rectangles.map((rect, i) => { return ( <Rectangle key={i} shapeProps={rect} isSelected={rect.id === selectedId} onSelect={() => { selectShape(rect.id); }} onChange={(newAttrs) => { const rects = rectangles.slice(); rects[i] = newAttrs; setRectangles(rects); }} /> ); })} </Layer> </Stage> ); }; export default App;