Как получить элементы SVG для заданных координат?

Я хотел бы получить элементы SVG для заданных координат.
Я пробовал использовать document.elementsFromPoint(x, y).
Однако он возвращает только основной элемент 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>

Свободный перевод вопроса How to get SVG elements by coordinates? от участника @Stefan.


Ответы (1 шт):

Автор решения: Alexandr_TT

Поскольку Document.elementsFromPoint() относится к области просмотра, вы можете использовать Element.getBoundingClientRect() это также относится к области просмотра.
Итак, положение робота (в центре) является результатом положения x / y плюс половина ширины / высоты.
И после нахождения массива элементов вы можете проверить, больше ли 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>

Свободный перевод ответа от участника @chrwahl.

→ Ссылка