Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you convert screen coordinates to document space in a scaled SVG?

Tags:

html

svg

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'});
});
like image 848
Jon Avatar asked Mar 04 '14 21:03

Jon


2 Answers

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>
like image 178
defghi1977 Avatar answered Nov 02 '22 14:11

defghi1977


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>
like image 29
Paul LeBeau Avatar answered Nov 02 '22 13:11

Paul LeBeau