携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情
实现示例
实现
主要用到Konva 组件:
- Rect
<Rect
x={20}
y={50}
width={100}
height={100}
fill="red"
shadowBlur={10}
/>
Rect设置draggable = true , 监听拖拽事件:onDragEnd、onDragStart、onDragMove等一系列事件,获得当前Rect的属性;监听形变相关事件:onTransform、onTransformEnd、onTransformStart,获得变化后Rect数据;
- Transformer 将当前需要变形的Rect的node赋值给Transformer组件
Transformer一些组件参数:
- keepRatio={false} // 拖动四角不保存比例
- rotateEnabled={false} // 禁止旋转
关键代码
rect组件代码
import React, { useState } from 'react';
import { Rect, Transformer } from 'react-konva';
import { Html } from 'react-konva-utils';
const Rectangle = ({
shapeProps,
canCancle = false,
isSelected, onSelect,
onDel = (v) => { },
onChange,
draggable = true, onDragStart = () => { }, onDragEnd = () => { }, onDragMove = () => { } }) => {
const shapeRef = React.useRef();
const trRef = React.useRef();
const [cancelPosition, setCancelPosition] = useState(shapeProps);
React.useEffect(() => {
if (isSelected) {
console.log('shwoProps',shapeProps);
trRef?.current?.nodes([shapeRef.current]);
trRef?.current?.getLayer().batchDraw();
}
}, [isSelected]);
return (
<React.Fragment>
{
{/* 删除按钮 */}
canCancle && <Html
divProps={{
style: {
position: 'absolute',
top: cancelPosition.y - 12,
color: 'red',
width: '16px',
height: '16px',
left: cancelPosition.x + cancelPosition.width + 6,
},
}}
>
<div className="cancelIcon"></div>
</Html>
}
<Rect
onDblClick={(e) => {
onSelect(e);
}} // 双击选中
onClick={(e) => {
onSelect(e);
}}
onTap={(e) => {
onSelect(e);
}}
onDragStart={(e) => {
onDragStart(e);
}}
onTransformStart={(e) => {
onDragStart(e);
}}
ref={shapeRef}
{...shapeProps}
draggable={draggable}
onDragMove={(e) => {
onDragMove();
onChange({
...shapeProps,
x: e.target.x(),
y: e.target.y(),
});
}}
onDragEnd={(e) => {
onDragEnd();
onChange({
...shapeProps,
x: e.target.x(),
y: e.target.y(),
});
}}
onTransform={(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(),
// set minimal value
width: Math.max(5, node.width() * scaleX),
height: Math.max(node.height() * scaleY),
});
}}
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(),
// set minimal value
width: Math.max(5, node.width() * scaleX),
height: Math.max(node.height() * scaleY),
});
}}
/>
{/* 变形控制 */}
{isSelected && (
<Transformer
keepRatio={false} // 拖动四角不保存比例
ref={trRef}
rotateEnabled={false} // 禁止旋转
boundBoxFunc={(oldBox, newBox) => {
// limit resize
if (newBox.width < 5 || newBox.height < 5) {
return oldBox;
}
return newBox;
}}
/>
)}
</React.Fragment>
);
};
export default Rectangle;
父组件调用
<Rectangle
key={i}
shapeProps={{
...rect,
stroke: 'blue',
strokeWidth: rect?.id === selectedId ? 3 : 1,
fill: 'transparent'
}}
isSelected={rect?.id === selectedId}
canCancle={rect?.id === selectedId}
onDel={handleDelRect}
onSelect={() => {
onConfirm();
}}
onDragStart={() => {
setIsDragging(true);
}}
onDragMove={() => {
setSelectedRect(rect);
setSelectedId(rect.id);
}}
onDragEnd={() => {
setSelectedRect(rect);
setSelectedId(rect.id);
setIsDragging(false);
}}
onChange={(newAttrs) => {
setRectangles(rects);
setSelectedRect(rect);
}}
/>