React 打字稿鼠标事件重叠

React typescript mouse events are overlapping

我正在使用 react with typescript。我使用 SVG 在其中绘制矩形。我正在处理两个功能,第一个是我必须在 SVG 中绘制任意数量的形状,另一个是允许鼠标拖动选项。现在,问题是即使我正在绘制一个形状然后在其中绘制另一个形状,第一个绘制的形状正在移动并且新的形状正在绘制。

我想做的是,如果我单击并移动形状,我的绘图矩形功能将不起作用,如果我正在绘制矩形,则已绘制的形状将不会移动。发生这种情况是因为我在两个逻辑中都使用了 mouseup 和 mousemove 事件,这就是它们崩溃的原因。我不知道把它们分开。

这是我的代码:

import "./styles.css";
import React, { useEffect, useRef } from 'react';


interface Iboxes{
  id: string,
  coordinates:{
    x: number
    y: number
    width: number
    height: number
  }
}


export default function App() {
  const divRef = useRef<HTMLDivElement>(null);
    const svgRef = useRef<SVGSVGElement>(null);
    const boxes: Array<Iboxes> = [];
    
    useEffect(() => {
      const containerSvg = svgRef.current;
      let p:DOMPoint;
      let w:number;
      let h:number;
      if(containerSvg){

        const svgPoint = (elem:any, x:number, y:number) => {
          const point = containerSvg.createSVGPoint();
          point.x = x;
          point.y = y;
          return point.matrixTransform(elem.getScreenCTM().inverse());
        };

        containerSvg.addEventListener('mousedown', (event) => {
          const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
          const start = svgPoint(containerSvg, event.clientX, event.clientY);
          const drawRect = (e:any) => {
            p = svgPoint(containerSvg, e.clientX, e.clientY);
            w = Math.abs(p.x - start.x);
            h = Math.abs(p.y - start.y);
            if (p.x > start.x) {
              p.x = start.x;
            }
            if (p.y > start.y) {
              p.y = start.y;
            }
        
            rect.setAttributeNS(null, 'x', p.x as unknown as string);
            rect.setAttributeNS(null, 'y', p.y as unknown as string);
            rect.setAttributeNS(null, 'width', w as unknown as string);
            rect.setAttributeNS(null, 'height', h as unknown as string);
            rect.setAttributeNS(null, 'class', 'svg_rec');
            rect.setAttributeNS(null, 'id', 'rec_' + boxes.length as unknown as string)
            

            containerSvg.appendChild(rect);
          };

      
          const endDraw = (e:any) => {
            containerSvg.removeEventListener('mousemove', drawRect);
            containerSvg.removeEventListener('mouseup', endDraw);
            boxes.push({id:'rec_' + boxes.length as unknown as string, coordinates:{x: p.x, y: p.y, width: w, height: h}})

            let offset:any;
            let selectedRect: SVGRectElement | null;
            rect.addEventListener('mousedown', startDrag);
            rect.addEventListener('mousemove', drag);
            rect.addEventListener('mouseup', endDrag);
            rect.addEventListener('mouseleave', endDrag);


            function startDrag(evt:any) {
              selectedRect = rect;
              if(selectedRect){
                offset = getMousePosition(evt);
                if(offset){
                  let rectX = selectedRect.getAttributeNS(null, 'x');
                  let rectY = selectedRect.getAttributeNS(null, 'y');
                  if(rectX && rectY){
                    offset.x -= parseFloat(rectX);
                    offset.y -= parseFloat(rectY);
                  }
                }
              }
            }

            function drag(evt:any) {
              if(selectedRect){
                var coord = getMousePosition(evt);
                if(coord && offset){
                  let x = coord.x - offset.x;
                  let y = coord.y - offset.y;
                  if(x && y){
                    selectedRect.setAttributeNS(null, "x", x as unknown as string);
                    selectedRect.setAttributeNS(null, "y", y as unknown as string);
                  }
                }
              }
            }

            function endDrag() {
              selectedRect = null;
            }

            function getMousePosition(evt:any) {
              var CTM = rect.getScreenCTM();
              if(CTM){
                return {
                  x: (evt.clientX - CTM.e) / CTM.a,
                  y: (evt.clientY - CTM.f) / CTM.d
                };
              }
            }
          };
          
          containerSvg.addEventListener('mousemove', drawRect);
          containerSvg.addEventListener('mouseup', endDraw);

        });
      }
    },[]);
    
  return (
    <div className="App">
      <div className='container' ref={divRef}>
          <svg id="svg" ref={svgRef}>
          </svg>
        </div>
    </div>
  );
}

我还创建了一个沙箱环​​境来演示我的问题:

here is a sandbox link

你只需要添加

evt.stopPropagation();

到第 79 行的函数 startDrag()

function startDrag(evt: any) {
  evt.stopPropagation();
  selectedRect = rect;

  // ...
}

这是fixed sandbox


编辑

因为顶部的其他矩形可能会遮挡当前拖动的矩形。一种解决方案是在拖动一个矩形时禁用其他矩形的 pointer-events

将以下代码添加到 startDrag 函数

containerSvg.classList.add("dragging");
selectedRect?.classList.add("target");

然后将以下代码添加到endDrag函数

containerSvg.classList.remove("dragging");
selectedRect?.classList.remove("target");

然后在您的 css 代码中,添加以下规则:

.dragging rect:not(.target) {
  pointer-events: none;
}

选中updated sandbox