如何使用 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(5, 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;
Transformer 不会做什么
Transformer 绘制控制点并应用缩放。吸附到其他对象、对齐辅助线、多选共享边界框以及每个形状的纵横比规则都需要由你自行构建——请参阅对象吸附了解一种实现方式。