如何通过坐标获取SVG元素?

How to get SVG elements by coordinates?

我想获取一些给定坐标的 SVG 元素。

我尝试使用 document.elementsFromPoint(x,y)。但是,它只是 returns 主 svg 元素本身,而不是 svg.

中的子元素(圆圈、路径等)

=>如何找到给定坐标的 SVG 元素?

示例 html 文件,我想通过按箭头键在绿色路径上移动红色圆圈。仅当圆圈保持在绿色路径上时才允许移动。

截图:

演示:

<html>

<head>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>

<body>
  <script>
    function onClick() {
      alert('You have clicked the circle.')
    }

    function onKeyPress(event) {
      switch (event.keyCode) {
        case 37:
          moveLeft();
          break;
        case 38:
          moveUp();
          break;
        case 39:
          moveRight();
          break;
        case 40:
          moveDown();
          break;
        default:
      }

    }

    function moveDown() {
      console.log('down');
      var path = d3.select('#path');

      var robot = d3.select('#robot');
      var cx = Number(robot.attr('cx'));
      var cy = Number(robot.attr('cy'));

      var newcy = cy + 10;
      var elements = document.elementsFromPoint(cx, newcy)
      if (path in elements) {
        robot.attr('cy', cy + 10);
      }

    }

    function moveUp() {
      console.log('up');
      var robot = d3.select('#robot');
      var cy = Number(robot.attr('cy'));
      robot.attr('cy', cy - 10);
    }

    function moveLeft() {
      console.log('left')
      var robot = d3.select('#robot');
      var cx = Number(robot.attr('cx'));
      robot.attr('cx', cx - 10);
    }

    function moveRight() {
      console.log('right');
      var robot = d3.select('#robot');
      var cx = Number(robot.attr('cx'));
      robot.attr('cx', cx + 10);
    }

    function onLoad() {
      console.log('onload')
      this.addEventListener('keydown', event => onKeyPress(event));
    }
  </script>

  <svg width='500px' height='500px' focusable onload="onLoad()">
            <text x='0' y='20' fill='blue'>Hello world from within svg! Press arrow keys to move the  circle:</text>            
            <path id="path" d="M100 100 L 100 200 L 200 200" stroke='green' fill="transparent"/>
            <circle id="robot" cx="100" cy="100" r="5" fill='red' onclick="onClick()" />
        </svg>
</body>

</html>

因为Document.elementsFromPoint() is relative to the viewport you can use Element.getBoundingClientRect() 这也是相对于视口的。因此,机器人位置(中心)是 x/y 位置加上一半 width/height 的结果。在找到元素数组后,您可以测试 indexOf() 是否大于 -1.

svg {
  margin: 15px;
}
<html>  

<body>
    <script>
        var step = 10;
        
        function onLoad(){
            console.log('onload')
            disableKeyboardScrolling();  
            this.addEventListener('keydown', event => onKeyPress(event));
        }

        function disableKeyboardScrolling(){
             window.addEventListener("keydown", function(e) {
                if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].indexOf(e.code) > -1) {
                    e.preventDefault();
                }
            }, false);
        }

        function onClick(){
            alert('You have clicked the circle.')
        }


        function onKeyPress(event){             
            switch(event.code){
                case "ArrowLeft": moveLeft();
                    break;
                case "ArrowUp": moveUp();
                    break;
                case "ArrowRight": moveRight();
                    break;
                case "ArrowDown": moveDown();
                    break;
                default:
            }
                
        }

        function moveDown(){
            console.log('down');                
                    
            var robot = document.getElementById('robot');
            var coordinates = getCenterCoordinates(robot);
            var newx = coordinates[0];
            var newy = coordinates[1] + step;   
            if (isOnAllowedPath(newx, newy)) {
                let cy = Number(robot.getAttribute('cy'))
                let newCy = cy + step;
                robot.setAttribute('cy', newCy);
            }
                        
        }           

        function moveUp(){
            console.log('up');

            var robot = document.getElementById('robot');
            var coordinates = getCenterCoordinates(robot);
            var newx = coordinates[0];
            var newy = coordinates[1] - step;   
            if (isOnAllowedPath(newx, newy)) {
                let cy = Number(robot.getAttribute('cy'))
                let newCy = cy-step;
                robot.setAttribute('cy', newCy);
            }           
        }

        function moveLeft(){
            console.log('left')  
                
            var robot = document.getElementById('robot');
            var coordinates = getCenterCoordinates(robot);
            var newx = coordinates[0] -step;
            var newy = coordinates[1];  
            if (isOnAllowedPath(newx, newy)) {
                let cx = Number(robot.getAttribute('cx'))
                let newCx = cx-step;
                robot.setAttribute('cx', newCx);
            }           
        }

        function moveRight(){
            console.log('right');

            var robot = document.getElementById('robot');
            var coordinates = getCenterCoordinates(robot);
            var newx = coordinates[0] +step;
            var newy = coordinates[1];  
            if (isOnAllowedPath(newx, newy)) {
                let cx = Number(robot.getAttribute('cx'))
                let newCx = cx+step;
                robot.setAttribute('cx', newCx);
            }                   
        }
        

        function getCenterCoordinates(robot){
            let rect = robot.getBoundingClientRect();
            let x = rect.x + rect.width / 2;
            let y = rect.y + rect.height / 2;
            return [x, y]
        }

        function isOnAllowedPath(x,y){
            var allowedPaths = document.getElementsByClassName('allowed-path'); 
            var elementsAtPosition = document.elementsFromPoint(x,y);
            for(allowedPath of allowedPaths){
                let isAllowed = elementsAtPosition.indexOf(allowedPath) > -1;
                if (isAllowed){
                    return true;
                }
            }
            return false;                   
        }

    </script>

    <svg 
        width='500px' 
        height='500px'  
        focusable       
        onload="onLoad()"
    >
        <text x='0' y='20' fill='blue'>Hello world from within svg! Please move the circle with arrow keys:</text>          
        <path class="allowed-path" d="M100 100 L 100 200 L 200 200" stroke='green' fill="transparent"/>
        <path class="allowed-path" d="M200 200 L 200 100 L 300 100" stroke='blue' fill="transparent"/>
        <circle id="robot" cx="100" cy="100" r="5" fill='red' onclick="onClick()" />
    </svg>
</body>
</html>