Sync HTML selection box with rotated SVG element (rotation drift & incorrect bounds)

13 hours ago 1
ARTICLE AD BOX

I am building a simple SVG editor (move / resize / rotate with custom pivot).
The SVG element is transformed using SVG transforms, but the selection box is NOT SVG — it is a HTML <div> overlay positioned on top of the SVG.

After rotating an SVG element:

The HTML selection box does not correctly follow the rotated SVG element

Bounding box is offset

Handles appear in incorrect positions

Errors increase at 90° / 180° rotations

Rotation does not follow the mouse correctly

Mouse angle drifts while dragging

Rotation center appears to move during rotation

Translation works correctly. Scaling works partially.

Rotation logic (SVG element)

const mouse = pt.matrixTransform(svg.getScreenCTM().inverse()); const wp = pivotPoint.matrixTransform( selectedElement.getCTM() ); const currentAngle = Math.atan2( mouse.y - wp.y, mouse.x - wp.x ); const delta = currentAngle - startAngle; const m = startMatrix .translate(pivot.x, pivot.y) .rotate(delta * 180 / Math.PI) .translate(-pivot.x, -pivot.y); selectedElement.transform.baseVal.initialize( svg.createSVGTransformFromMatrix(m) );

HTML selection box update

const bbox = selectedElement.getBBox(); const ctm = selectedElement.getCTM(); const p1 = svg.createSVGPoint(); p1.x = bbox.x; p1.y = bbox.y; const p2 = svg.createSVGPoint(); p2.x = bbox.x + bbox.width; p2.y = bbox.y + bbox.height; const t1 = p1.matrixTransform(ctm); const t2 = p2.matrixTransform(ctm); const m = selectedElement.transform.baseVal.consolidate()?.matrix; const angle = m ? Math.atan2(m.b, m.a) * 180 / Math.PI : 0; // HTML div overlay selectorDiv.style.transform = `translate(${Math.min(t1.x, t2.x)}px, ${Math.min(t1.y, t2.y)}px) rotate(${angle}deg)`; selectorDiv.style.width = `${Math.abs(t2.x - t1.x)}px`; selectorDiv.style.height = `${Math.abs(t2.y - t1.y)}px`;

HTML selection box structure

<div id="selector"> <div class="path"> <div class="point anchor"></div> <!-- resize handles --> <div class="point p1" data-handle="nw"></div> <div class="point p2" data-handle="n"></div> <div class="point p3" data-handle="ne"></div> <div class="point p4" data-handle="w"></div> <div class="point p5" data-handle="e"></div> <div class="point p6" data-handle="sw"></div> <div class="point p7" data-handle="s"></div> <div class="point p8" data-handle="se"></div> <!-- rotation handle --> <div class="point rotate"></div> </div> </div>

Questions

Is getBBox() + getCTM() sufficient when syncing an HTML overlay with a rotated SVG element?

Why does rotation drift when the SVG element already has transforms applied?

Should rotation math be done in local SVG space or world (screen) space?

Is there a recommended approach for HTML-based selection boxes over SVG (used by editors like Figma / Illustrator)?

Thanks for taking the time to look into this.

Any pointers, corrections, or references to established approaches are appreciated.

Read Entire Article