jaapz
jaapz

Reputation: 1009

Translating browser click event `x` and `y` coordinates to coordinates on a scaled Snap svg element

I needed to add a piece of text to an svg rendered by snap, at the point where the user clicks.

Because the SVG is scaled using 100% width and uses the viewBox attribute, I needed to translate the x and y coordinates provided by the browser's click event to x and y coordinates on the scaled svg.

I managed to make this work by using this piece of code:

var $canvas = $('svg#myscaledsvg');
var snap = new Snap($canvas[0]);

snap.mousedown(function(event) {
    var offset = $canvas.offset();
    var matrix = snap.transform().diffMatrix.invert();

    var x = matrix.x(event.pageX - offset.left, event.pageY - offset.top);
    var y = matrix.y(event.pageX - offset.left, event.pageY - offset.top)

    // below function call actually renders the text element to the given x and y coords       
    addSomeText(x, y);
});

Now, I understand why I need to subtract the offsets. What I don't understand is what snap.transform().diffMatrix.invert() actually does.

Can someone enlighten me why this works? I can't get much from the snap documentation, which seems rather sparse overall.

Upvotes: 4

Views: 696

Answers (1)

Ian
Ian

Reputation: 13842

I'm guessing in this case, you can swap diffMatrix for globalMatrix and it will still work ?

Snap stores 3 matrices on response to a transform() method. localMatrix, diffMatrix and globalMatrix.

localMatrix is the transform existing on the element itself

globalMatrix is the matrix from the viewPort to the element that is being applied

diffMatrix is the difference between them.

For globalMatrix it may be worth looking at getCTM as it passes through to this native method. For localMatrix look at any transforms on the element itself (likely none on an svg element itself as it doesn't support transforms).

So when you are looking at your browser, the view may be zoomed in, panned etc, have a viewBox. The coordinates of the mouse, need to be converted from this coordinate space, to the one the element is in. Thats what the inverse of globalMatrix will do, to convert from the mouse. So I'm assuming diffMatrix and globalMatrix (getCTM) will probably mean the same in this case (because its an svg element with no transform applied).

If this doesn't really make sense, it may be worth doing a bit of extra looking at svg coordinate space tutorials as it can be quite helpful.

Edit: I'm adding a bit more info to try and help below...

jsfiddle I've done, has an example using similar code to the OP.

jsfiddle 2 similar but responsive, look in the console log to see the matrix scale vary

The SVG is basically scaled by 4, so zoomed in with the following...

    height="800" width="800" viewBox="0 0 200 200"

or

    height="100%" width="100%" viewBox="0 0 200 200"

There is a circle at 25,25, click on the circle in the fiddle.

The mouse event will be round about 100,100 in user/browser event space (there is some offset there to consider, so maybe 108,108 or so depending).

So with a scale of 4, we need to convert that 100,100 to account for that scale, otherwise it will appear off the screen.

If everything we will display on the svg will be zoomed by 4, we only need to use 1/4 of the value to display at the correct coordinates is possibly a useful simplistic way of looking at it.

How do we do that? We invert the scale (or whatever transform is there). What we really want is a scale of 0.25 to convert that 100,100 to 25,25 so it appears at the correct places on the screen, because everything is zoomed in (as everything will get scaled by 4 by the svg parameters).

And yes, localMatrix doesn't really make much sense on the svg element itself (but its not a problem either). It would make sense if it was a group with a transform on it or some other element.

Its also worth noting that Snap passes parameters x,y into the mousedown func, so you could probably use that, rather than event.pageX/pageY.

Upvotes: 1

Related Questions