Search code examples
jquerysvgclickviewbox

Why movement of SVG object is so unpredictable?


I am trying to move an SVG object using click() method, but the object is not moving where I want it to move.

I want my object to move exactly where user's screen was clicked!

Take a look at what I am trying :

 $(function() {
    // alert("click somewhere on your screen");
    $(window).on("click", function(e) {
      var xco = e.pageX;
      var yco = e.pageY;
      $("circle").animate({
        "cx": xco,
        "cy": yco
      }, 1000);
    });
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<svg viewBox="10 10 80 80" style="height:80vh; width:80vw; border:2vw solid black;">
    <circle cx="20" cy="10" r="5" fill="yellow" stroke-width="1" stroke="black" />
</svg>


Solution

  • The coordinates you are using are for the page. Inside an svg. First, you want the coordinates from the svg origin. In addition, you are setting an viewBox, which changes how those coordinates are interpreted. Fortunately, javascript provides a couple of functions and objects that can help. Mainly, you need to read about getScreenCTM(). Below is an example with a function that takes care of the transformation:

    function transformAbsPoint(x, y){
      var svg = $("#svgel")[0];
      var pt = svg.createSVGPoint();
      pt.x = x;
      pt.y = y;
      var loc = pt.matrixTransform(svg.getScreenCTM().inverse());
      return [loc.x, loc.y];
    }
    
     $(function() {
        // alert("click somewhere on your screen");
        $(window).on("click", function(e) {
          var pt = transformAbsPoint(e.clientX, e.clientY);
          $("circle").animate({
            "cx": pt[0],
            "cy": pt[1]
          }, 1000);
        });
      });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <svg id="svgel" viewBox="10 10 80 80" style="height:80vh; width:80vw; border:2vw solid black;">
        <circle cx="20" cy="10" r="5" fill="yellow" stroke-width="1" stroke="black" />
    </svg>