如何使用 React 和 Konva 缩放及旋转 Canvas 图形?
目前,Transformer 工具没有完全声明式的 React 用法。你需要对 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) { // we need to attach transformer manually 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) => { // transformer is changing scale of the node // and NOT its width or height // but in the store we have only width and height // to match the data better we will reset scale on transform end const node = shapeRef.current; const scaleX = node.scaleX(); const scaleY = node.scaleY(); // we will reset it back node.scaleX(1); node.scaleY(1); onChange({ ...shapeProps, x: node.x(), y: node.y(), // set minimal value width: Math.max(5, node.width() * scaleX), height: Math.max(node.height() * scaleY), }); }} /> {isSelected && ( <Transformer ref={trRef} flipEnabled={false} boundBoxFunc={(oldBox, newBox) => { // limit resize 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) => { // deselect when clicked on empty area 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;
Transformer 不会做什么
Transformer 绘制控制手柄并应用缩放。对象吸附、对齐参考线、多选共用包围盒和各图形的宽高比规则都需要你自行实现。你可以参考对象吸附中的方法。