Home > Blockchain >  How to get SVG elements by coordinates?
How to get SVG elements by coordinates?

Time:09-30

I would like to get the SVG elements for some given coordinates.

I tried to use document.elementsFromPoint(x,y). However, it only returns the main svg element itself, not the sub elements (circles, paths etc) inside svg.

=>How can I find the SVG elements for given coordinates?

Example html file where I want to move a red circle on a green path by pressing arrow keys. Movement should only be allowed if the circle stays on on the green path.

Screenshot:

enter image description here

Demo:

<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>

CodePudding user response:

Because Document.elementsFromPoint() is relative to the viewport you can use Element.getBoundingClientRect() that also is relative to the viewport. So, the robot position (the center) is the result of the x/y position plus half the width/height. And after finding the array of elements you can test if indexOf() is more than -1.

svg {
  margin: 15px;
}
<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 path = document.getElementById('path');

      var robot = d3.select('#robot');
      var cx = Number(robot.attr('cx'));
      var cy = Number(robot.attr('cy'));
      
      let robotrect = document.querySelector('#robot').getBoundingClientRect();
      let posx = robotrect.x   robotrect.width / 2;
      let posy = robotrect.y   robotrect.height / 2;

      var newcy = cy   10;
      var elements = document.elementsFromPoint(posx, posy   10);

      if (elements.indexOf(path) > -1) {
        robot.attr('cy', newcy);
      }
    }

    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>

  • Related