携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
实现示例
实现思路
- Rect组件实现table外边框绘制,详情可看Rect绘制边框
- Line组件 绘制table线 计算鼠标位置是否在table内,记录mouseUp和mouseDown的坐标值,计算横竖现
- 通过记录table,line数据绘制table
关键代码
父组件调用代码
<Group>
{
tableBorder && JSON.stringify(tableBorder) !== '{}' && <Rectangle
canCancle={true}
draggable={false}
onDel={handleDelTable}
shapeProps={tableBorder}
isSelected={true}
onSelect={(e) => {
console.log('onSelect', e); // 当前变更信息
}}
onChange={(newAttrs) => {
console.log('newAttrs', newAttrs); // 当前变更信息
}}
/>
}
{vLines && vLines.length > 0 && vLines.map((point, i) => (
<TableLine
onDel={e => handleDelLine(e, i, 'vLines')}
key={i}
points={point}
/>
))}
{hLines && hLines.length > 0 && hLines.map((point, i) => (
<TableLine
canCancle={hLines?.length > 1}
onDel={e => handleDelLine(e, i, 'hLines')}
key={i}
points={point}
/>
))}
<TableLine canCancle={false} points={linePoints} visible={lineVisible} />
</Group>
TableLine组件代码
// 画布
import React, { useState, useRef ,useEffect} from 'react';
import { Line, Transformer } from 'react-konva';
import { Html } from 'react-konva-utils';
export default function TableLine({
points,
onDel = (v) => {},
canCancle = true,
visible = true
}) {
const [point,setPoint] = useState(points)
const [cancelPosition, setCancelPosition] = useState(point);
const [isSelected, setIsSelected] = useState(false);
useEffect(() => {
setCancelPosition(points);
setPoint(points);
},[points])
return (
<>
{
canCancle && <Html
divProps={{
style: {
position: 'absolute',
top: cancelPosition[3] - 8,
left: cancelPosition[2] - 7,
color: 'red',
width: '16px',
height: '16px',
},
}}
>
<div className="cancalBtn"></div>
</Html>
}
<Line
visible={visible}
points={point} // [x1, y1, x2, y2, x3, y3]
stroke="blue"
lineJoin='round'
lineCap='round'
dash={[5, 10]}
// draggable
/>
</>
);
}
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;