将鼠标事件添加到 svg <pattern>,检测网格悬停

IT技术 javascript reactjs svg
2021-05-13 01:48:06

我有一个有点复杂的问题。我正在尝试通过 svg 呈现网格,然后向网格添加事件侦听器。目前我正在通过一个<pattern>元素渲染网格我对渲染网格的其他途径持开放态度,但它需要具有可扩展性/高性能,因为这个网格很容易成为 10 个数千个正方形。把它想象成平面图或蓝图。

我想要什么:我希望能够将事件侦听器附加到grid 的每个方块

我所做的:我查阅了 svg 文档,尝试了很多不同的东西(比如 onclick 处理程序,svg 属性,比如指针事件……等等),但没有运气。我可能可以让它与鼠标客户端坐标一起工作,但是如果可能的话,我想避免使用这种方法,因为这个 svg 将具有缩放和平移功能......这会使坐标转换变得痛苦。

基本 svg 代码(针对此问题进行了简化):

<svg viewBox="0 0 100 100">
  <g className="view-control">
    <defs>
      <pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse">
        <path d="M 10 0 L 0 0 0 10" fill="none" stroke="gray" strokeWidth="0.5"/>
      </pattern>
    </defs>
    <rect width="100%" height="100%" fill="url(#grid)" />
  </g>
</svg>

小提琴演奏

2个回答

我不确定您对“鼠标客户端坐标”的反对意见是什么,但这很简单,并且符合您的要求。

  1. 将单击处理程序添加到网格矩形。我在这个演示中使用了一个点击处理程序。但是如果你想基于悬停,你可以使用 mousemove 事件代替。

    <rect width="200%" height="200%" fill="url(#grid)" onClick={(e) => handleClick(e)}/>
    
    function handleClick(e) {
      var pos = getSVGPosition(e);
      createRectAt(pos);
    }
    
  2. 将鼠标坐标转换为 svg 坐标的函数如下所示:

    function getSVGPosition(e) {
      var svg = e.nativeEvent.target.ownerSVGElement;
      var pt = svg.createSVGPoint();
      pt.x = e.nativeEvent.clientX;
      pt.y = e.nativeEvent.clientY;
      pt = pt.matrixTransform(svg.getScreenCTM().inverse());
      return {svg: svg, x: pt.x, y: pt.y};
    }
    
  3. 然后为了演示的目的,我在相关的网格位置创建了一个正方形

    function createRectAt(pos) {
      var rect = document.createElementNS(pos.svg.namespaceURI, "rect");
      rect.setAttribute("x", Math.floor(pos.x / 10) * 10);
      rect.setAttribute("y", Math.floor(pos.y / 10) * 10);
      rect.setAttribute("width", 10);
      rect.setAttribute("height", 10);
      rect.setAttribute("fill", "green");
      pos.svg.appendChild(rect);
    }
    

https://jsfiddle.net/tp530748/31/

如果您将网格<pattern>设为a,则您将无法在图案上注册任何鼠标事件。模式命中框将覆盖整个<rect>

我建议根据 的大小<svg>、viewBox 和网格的间距自己计算正方形的坐标将任何鼠标事件附加到 SVG 元素