【问题标题】:React typescript mouse events are overlapping反应打字稿鼠标事件重叠
【发布时间】:2022-01-04 07:52:27
【问题描述】:

我正在使用对打字稿做出反应。我使用 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

【问题讨论】:

    标签: javascript reactjs typescript svg


    【解决方案1】:

    你只需要添加

    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

    【讨论】:

    • 还是有问题。如果您将选择的形状移动到现有形状上,它将丢弃选择的形状
    • @SagharFrancis 它没有被丢弃,但被顶部的矩形遮挡 - 考虑将当前拖动的矩形置于顶部,或禁用 pointer-events 用于其他矩形。检查我更新的答案。
    猜你喜欢
    • 2017-06-24
    • 1970-01-01
    • 2021-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-11
    • 2022-01-12
    • 1970-01-01
    相关资源
    最近更新 更多