react-konva实现鼠标框选功能

·  阅读 60

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

功能介绍

鼠标框选一段,实现框选可拖拽更改; 框选内容转图片

鼠标框选

主要实现思路:

监听鼠标事件

onMouseDown onMousemove onMouseUp 主要Stage上监听这几个鼠标事件

拿到鼠标坐标
const { x, y } = StageRef.current.getPointerPosition();
复制代码
绘制方框

这里主要通过设置一个固定的框选Rect,onMouseDown时设置Rect的visible为true,onMouseUp时设置visible为false。并添加rect到Rect列表,重制选中Rect属性

const areaAttr = {
      x: Math.min(tranLeft, x2),
      y: Math.min(tranTop, y2),
      width: Math.abs(x2 - tranLeft),
      height: Math.abs(y2 - tranTop),
      visible: true,
    };
    setSelectionRectAttrs(Object.assign({}, selectionRectAttrs, areaAttr));
复制代码

选中内容转图片

konva的toDataURL 方法即可

const data = ImageRef.current.toDataURL({
      x: rect.x,
      y: rect.y,
      width: rect.width,
      height: rect.height,
    });
复制代码

关键代码

// 画布
import React, { useState, useRef, useEffect } from 'react';
import { Stage, Layer, Image, Group } from 'react-konva';
import useImage from 'use-image';
import Rectangle from './Rectangle';
import { Html } from 'react-konva-utils';
const initialRectangles = [
  {
    x: 0,
    y: 0,
    width: 150,
    height: 100,
    stroke: 'blue',
    id: 'rect0',
  },
  {
    x: 150,
    y: 150,
    width: 50,
    height: 150,
    stroke: 'blue',
    id: 'rect1',
  },
];

export default function ImageStage({ ref, ImageUrl, height, width, onConfirm }) {
  const [image] = useImage(ImageUrl);
  const [rectangles, setRectangles] = useState(initialRectangles);
  const [selectedId, setSelectedId] = useState(null);
  const [selectedRect, setSelectedRect] = useState({
    x: 0, y: 0, height: 0, width: 0,
  });
  const [isMoving, setIsmoving] = useState(false);  //  缩放比例
  const [isDragging, setIsDragging] = useState(false);
  const [conFirmVal, setConFirmVal] = useState('');

  const [x, setX] = useState(0);
  const [y, setY] = useState(0);
  const [tranLeft, setTranLeft] = useState(0);
  const [tranTop, setTranTop] = useState(0);
  const [selectionRectAttrs, setSelectionRectAttrs] = useState({
    x: 250,
    y: 250,
    width: 50,
    height: 50,
    stroke: 'blue',
    fill: 'rgba(0,0,255,0.5)',
    id: 'selectionRect',
    visible: false,
  });

  const ImageRef = useRef();
  const StageRef = useRef();


  async function handleGetImageUrl(rect = selectedRect) {
    setConFirmVal('');
    const data = ImageRef.current.toDataURL({
      x: rect.x,
      y: rect.y,
      width: rect.width,
      height: rect.height,
    });
    const result = await putUploadUrl(data);
    if(result && result.TextDetections?.length > 0){
      let text = result.TextDetections.map(i => {
        return i.DetectedText
      })
      setConFirmVal(text.join(''))
    }else{
      setConFirmVal('')
    }
  }
  
  // 当前鼠标是否在框选内,如果在框选内,则不能再添加框选
  function canMove(x, y) {
    const list = rectangles.filter(rec => (rec.x <= x && x <= rec.x + rec.width) && rec.y <= y && y <= rec.y + rec.height);
    return list.length <= 0;
  }
  function onMouseDown(e) {
    const tranLeft = (e.evt.offsetX + x / stageScale);
    setTranLeft(tranLeft);
    const tranTop = (e.evt.offsetY + y / stageScale);
    setTranTop(tranTop);
    // TODO:判断是否mousedown选中了其他可拖动的框
    setIsmoving(canMove(tranLeft, tranTop));
  }
  function onMousemove(e) {
    if (!isMoving || isDragging) return;

    const { x, y } = StageRef.current.getPointerPosition();
    const x2 = x / stageScale;
    const y2 = y / stageScale;
    // todo 框选范围太大取消框选
    const areaAttr = {
      x: Math.min(tranLeft, x2),
      y: Math.min(tranTop, y2),
      width: Math.abs(x2 - tranLeft),
      height: Math.abs(y2 - tranTop),
      visible: true,
    };
    setSelectionRectAttrs(Object.assign({}, selectionRectAttrs, areaAttr));
  }
  function onMouseUp(e) {
    if (!selectionRectAttrs.visible) {
      return;
    }
    setIsmoving(false);
    const id = `'rect' + ${rectangles.length}`;
    const newRec = Object.assign({}, selectionRectAttrs, {
      id,
      fill: 'transparent',
    });
    setRectangles([...rectangles, newRec]);
    setSelectedId(id);
    setSelectedRect(newRec);
    handleGetImageUrl(newRec);
    setSelectionRectAttrs(Object.assign({}, selectionRectAttrs, {
      visible: false,
    }));
  }
  function handleScaleBig() {
    setStageScale(stageScale + 0.1);
  };
  function handleScaleSmall() {
    setStageScale(stageScale - 0.1);
  };
  const checkDeselect = (e) => {
    console.log('checkDeselect', e);
  };
  const handleRotation = () => {
    const currentRef = StageRef.current;
  };
  return (
    <div className="stage-view" id="stage-view" >

      <Stage
        ref={StageRef}
        onMouseUp={onMouseUp}
        onMousemove={onMousemove}
        onMouseDown={onMouseDown}
        onTouchStart={checkDeselect
        width={width}
        height={height}
      >
        {/* 图片底图图层 */}
        <Layer
          width={width}
          height={height}
        >
          <Image
            width={width}
            height={height}
            ref={ImageRef}
            image={image} />
          {rectangles.map((rect, i) => (
            <Rectangle
              key={i}
              shapeProps={rect}
              isSelected={rect.id === selectedId}
              onSelect={() => {
                setSelectedRect(rect);
                setSelectedId(rect.id);
              }}
              onDragStart={() => {
                setIsDragging(true);
              }}
              onDragMove={() => {
                setSelectedRect(rect);
                setSelectedId(rect.id);
              }}
              onDragEnd={() => {
                setSelectedRect(rect);
                setSelectedId(rect.id);
                setIsDragging(false);
                handleGetImageUrl(rect);
              }}
              onChange={(newAttrs) => {
                const rects = rectangles.slice();
                rects[i] = newAttrs;
                setRectangles(rects);
                setSelectedRect(rect);
                setSelectedId(rect.id);
              }}
            />
          ))}
          {/* 框选rect */}
          <Rectangle
            shapeProps={selectionRectAttrs}
            isSelected={selectionRectAttrs.visible}
            onSelect={() => {
              setSelectedId(null);
            }}
            onChange={(newAttrs) => {
              console.log('newAttrs', newAttrs); //  当前变更信息
            }}
          />
          <Group >

          </Group>
        </Layer>
      </Stage>
    </div>

  );
}
复制代码
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改