SVG Relative Touch/Mouse Events
const hasTouch =
navigator.maxTouchPoints != null && navigator.maxTouchPoints > 0;
const el = document.body.appendChild(document.createElement('div'));
el.innerHTML = `
<svg id="mainSvg" width="100%" height="100%" viewBox="0 0 800 800">
<g transform="translate(100, 100) scale(0.8, 0.8) rotate(25, 400, 400)">
<rect x="0" y="0" width="800" height="800" fill="#ccc" stroke="none"/>
<text x="10" y="30" font-size="30px">click/tap the box</text>
</g>
</svg>
<style>
svg, div, body, html {
height: 100%;
width: 100%;
margin: 0; padding: 0;
}
svg {
overflow: hidden;
}
</style>
`;
function createSvg(type) {
return document.createElementNS('http://www.w3.org/2000/svg', type);
}
// mouse or touch location is always relative to the svg
// any css transformations on the svg are only accounted for in
// Chrome unfortunately
function svgTouch(e, svgEl) {
const touches = e.touches;
let locX, locY;
if (touches != null && touches.length > 0) {
locX = touches[0].clientX;
locY = touches[0].clientY;
} else {
locX = e.clientX;
locY = e.clientY;
}
const pt = svgEl.createSVGPoint();
pt.x = locX;
pt.y = locY;
const newPnt = pt.matrixTransform(svgEl.getScreenCTM().inverse());
return { locX: newPnt.x, locY: newPnt.y };
}
document.addEventListener(hasTouch ? 'touchstart' : 'mousedown', e => {
// global id `mainSvg` :P
const { locX, locY } = svgTouch(e, mainSvg);
const circle = createSvg('circle');
circle.cx.baseVal.value = locX;
circle.cy.baseVal.value = locY;
circle.r.baseVal.value = 20;
circle.style.fill = 'blue';
mainSvg.appendChild(circle);
});
This snippet shows how to get relative mouse/touch coordinates within an SVG element. The main trick here is on line 50… pt.matrixTransform(svgEl.getScreenCTM().inverse());
– we get the inverse transformation matrix of the “mainSvg” node and transform the mouse/touch coordinates by it.
Beware, when this was posted, as noted in the comments – CSS transforms on the SVG element or any of its parents won’t be accounted for in the `getScreenCTM` call in any browsers other than Chrome. There is an open Firefox issue for this I believe…