【问题标题】:How to get SVG elements by coordinates?如何通过坐标获取 SVG 元素?
【发布时间】:2021-11-21 12:01:49
【问题描述】:

我想获取一些给定坐标的 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>

【问题讨论】:

  • document.elementsFromPoint 也适用于 SVG 元素,但它是相对于视口的,而 cx 和 cy 是相对于 svg 的 viewBox 的。在这里,您需要添加身体的填充。此外,elements 将是一个数组,您不会使用 in 检查数组是否包含元素。

标签: javascript svg


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 2013-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-22
    • 2019-09-06
    • 2013-07-07
    • 2011-01-16
    • 2011-10-20
    相关资源
    最近更新 更多