react-konva实践:使用konva画table

·  阅读 17

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

实现示例

Aug-12-2022 15-24-21.gif

实现思路

  1. Rect组件实现table外边框绘制,详情可看Rect绘制边框
  2. Line组件 绘制table线 计算鼠标位置是否在table内,记录mouseUp和mouseDown的坐标值,计算横竖现
  3. 通过记录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;
复制代码
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改