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.
