I was working on an application to track movement in an SVG viewbox relative to the size of the SVG element itself. The sample code below has been reduced to remove any extra functions, variables, calculations, etc. as much as possible to demonstrate just the problem.
When moving the mouse cursor across the SVG, the green (purple & yellow edits added per comments) pointer tracker its x & y positions by 10px each when moving through the approximate zones marked by the red bands. Also moving left to right vs. right to left has slightly different trigger points.
const svg = document.getElementById('svg1');
svg.addEventListener('pointermove', event => {
// console.log(event.offsetX);
const id1 = 'pointer1';
const id2 = 'pointer1';
const id3 = 'pointer3';
const pointerOld1 = document.getElementById(id1);
if (pointerOld1) pointerOld1.remove();
const pointerOld2 = document.getElementById(id2);
if (pointerOld2) pointerOld2.remove();
const x = event.offsetX;
const y = event.offsetY;
const pointerNew1 = document.createElementNS(svg.namespaceURI, 'circle');
pointerNew1.setAttributeNS(null, 'id', id1);
pointerNew1.setAttributeNS(null, 'cx', x * 0.25);
pointerNew1.setAttributeNS(null, 'cy', y * 0.5);
pointerNew1.setAttributeNS(null, 'r', '15px');
pointerNew1.setAttributeNS(null, 'stroke', 'green');
pointerNew1.setAttributeNS(null, 'stroke-width', '5px');
pointerNew1.setAttributeNS(null, 'fill', 'none');
svg.append(pointerNew1);
const point = new DOMPoint(x, y).matrixTransform(svg.getCTM().inverse());
const pointerNew2 = document.createElementNS(svg.namespaceURI, 'circle');
pointerNew2.setAttributeNS(null, 'id', id1);
pointerNew2.setAttributeNS(null, 'cx', point.x);
pointerNew2.setAttributeNS(null, 'cy', point.y);
pointerNew2.setAttributeNS(null, 'r', '15px');
pointerNew2.setAttributeNS(null, 'stroke', 'purple');
pointerNew2.setAttributeNS(null, 'stroke-width', '5px');
pointerNew2.setAttributeNS(null, 'fill', 'none');
svg.append(pointerNew2);
const pointer3 = document.getElementById(id3);
if (!pointer3) {
const pointer3 = document.createElementNS(svg.namespaceURI, 'circle');
pointer3.setAttributeNS(null, 'id', id3);
pointer3.setAttributeNS(null, 'r', '10px');
pointer3.setAttributeNS(null, 'stroke', 'yellow');
pointer3.setAttributeNS(null, 'stroke-width', '5px');
pointer3.setAttributeNS(null, 'fill', 'none');
svg.append(pointer3);
}
else {
pointer3.setAttributeNS(null, 'cx', x * 0.25);
pointer3.setAttributeNS(null, 'cy', y * 0.5);
}
});
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 100" width="400" height="200"
style="border: 2px solid;">
<line x1="0" y1="0" x2="100px" y2="100" stroke="blue" />
<line x1="100" y1="0" x2="0" y2="100" stroke="blue" />
<rect x="14" y="0" width="1" height="100" fill="red"></rect>
<rect x="16" y="0" width="1" height="100" fill="red"></rect>
<rect x="18" y="0" width="1" height="100" fill="red"></rect>
<rect x="20" y="0" width="1" height="100" fill="red"></rect>
<rect x="22" y="0" width="12" height="100" fill="red"></rect>
<rect x="75" y="0" width="1" height="100" fill="red"></rect>
<rect x="77" y="0" width="12" height="100" fill="red"></rect>
</svg>
I found some "work-arounds" including:
- If I uncomment the
console.log(event.offsetX);
line, it performs fine. - If I move the x & y constant declarations before the
if (pointerOld) pointerOld.remove();
line, it also works fine.
However, I am trying to understand why it is behaving like this in the first place? Am I doing something wrong? Or is this a bug (in HTML/JavaScript/SVG/web browser/OS)?
2
Answers
First of all, like herrstrietzel write in the comment, the coordinate system for the SVG is different from the HTML page. Use the
screenToSVG()
function mentioned here.Second, you don’t need to create a new circle element on each event call back. Reuse the one that is already there.
your viewbox is for
100x100
(position), and your svg size is400x200(px)
so there is -50 and +50 to use in coordinates.