14

I'm using the svg jquery plugin by Keith Wood, not the HTML5 canvas.

I define my svg image like this to scale my svg triangle image to fit its div container:

  <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" viewBox="0 0 299 215" >
    <g>
    <polygon points="1,1 299,1 149,210" fill="blue" stroke="blue" stroke-width="0" class="votenow"/>
    </g>
    </svg>

But how do I then match the coordinate systems?

I want to capture the mouse location at some point over the triangle and draw a circle at those X Y coordinates, but the circle gets drawn in a different location because the coordinate systems don't match.

So a circle would be drawn at point 10,10 but appear to be at 50,60 for example.

How do people cope with this?

Thanks.

Final Solution: Using the JQuery plugin to draw the circle and getScreenCTM() to calculate the points.

Perhaps I no longer require the JQuery plugin but it will do for now. Couldn't see how to do it using only the plugin.

$('#cvtriangle .tri').on( "click", function(e) {
    jqsvg = $('#cvtriangle').svg('get');
    svg = document.querySelector("svg");
    var pt = svg.createSVGPoint();
    pt.x = e.clientX;
    pt.y = e.clientY;
    pt = pt.matrixTransform(svg.getScreenCTM().inverse());
    jqsvg.circle(pt.x, pt.y, 5, {class: 'vote', fill: 'white', stroke: 'white', strokeWidth: 2, cursor: 'pointer'});
});
Alexandr_TT
  • 13,635
  • 3
  • 27
  • 54
Jon
  • 155
  • 1
  • 1
  • 8

2 Answers2

16

See http://msdn.microsoft.com/en-us/library/hh535760%28v=vs.85%29.aspx It's my sample code. For this usage, getScreenCTM method is very useful.

    <svg viewBox="0 0 300 300" onload="
        var c = document.getElementById('c');
        var cx = c.cx.baseVal;
        var cy = c.cy.baseVal;
        var svg = this;
        var point = svg.createSVGPoint();
        svg.onmousemove = function(e){
            point.x = e.clientX;
            point.y = e.clientY;
            var ctm = c.getScreenCTM();
            var inverse = ctm.inverse();
            var p = point.matrixTransform(inverse);
            cx.value = p.x;
            cy.value = p.y;
        };
    ">
        <rect width="100%" height="100%" fill="yellow"/>
        <circle id="c" r="10" fill="blue"/>
    </svg>
ceving
  • 21,900
  • 13
  • 104
  • 178
defghi1977
  • 5,081
  • 1
  • 30
  • 29
  • OK, I got it working using your example code to get the mapping and the JQuery SVG plugin to draw the circles. – Jon Mar 06 '14 at 20:59
12

If you call the function getScreenCTM() on the SVG element, it will return the transform matrix used to convert document coordinates to screen coordinates. You want the transform matrix for the other direction, so call inverse() on the matrix object.

var transform = svg.getScreenCTM().inverse();

Now you can transform a point object to do the final conversion:

pt = pt.matrixTransform(transform);

Working demo here

var x = document.getElementById("x"),
    y = document.getElementById("y"),
    svg = document.querySelector("svg");

svg.addEventListener("mousemove", function(evt) {
    var pt = svg.createSVGPoint();
    pt.x = evt.pageX;
    pt.y = evt.pageY;
    pt = pt.matrixTransform(svg.getScreenCTM().inverse());

    x.innerHTML = pt.x;
    y.innerHTML = pt.y;
}, false);
#container {
    width: 200px;
    height: 200px;
}
div {
    float: left;
    margin-left: 1em;
}
<div id="container">
    <svg version="1.0" viewbox="0 0 100 100">
        <rect x="0" y="0" width="100" height="100" fill="blue"/>
    </svg>
</div>

<div>
    x = <span id="x"></span><br/>
    y = <span id="y"></span>
</div>

If the above version (using pageX/Y) doesn't work for you, try this version instead.

var x = document.getElementById("x"),
    y = document.getElementById("y"),
    svg = document.querySelector("svg");

svg.addEventListener("mousemove", function(evt) {
    var pt = svg.createSVGPoint();
    pt.x = evt.clientX;
    pt.y = evt.clientY;
    pt = pt.matrixTransform(evt.target.getScreenCTM().inverse());

    x.innerHTML = pt.x;
    y.innerHTML = pt.y;
}, false);
#container {
    width: 200px;
    height: 200px;
}
div {
    float: left;
    margin-left: 1em;
}
<div id="container">
    <svg version="1.0" viewbox="0 0 100 100">
        <rect x="0" y="0" width="100" height="100" fill="blue"/>
    </svg>
</div>

<div>
    x = <span id="x"></span><br/>
    y = <span id="y"></span>
</div>
Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
  • For some reason pt.x = evt.pageX didn't work but pt.x = evt.clientX did, so I went with defghi1977's answer. Cheers, and thank for the detailed answer. – Jon Mar 06 '14 at 21:04