Major update for v1 and tests
This commit is contained in:
60
src/utils/konvaCoords.js
Normal file
60
src/utils/konvaCoords.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import { MIN_INLINE_EDIT_HEIGHT, MIN_INLINE_EDIT_WIDTH } from '../constants/elements';
|
||||
|
||||
/**
|
||||
* Konva node coordinate helpers.
|
||||
*
|
||||
* The inline text editor (S2) needs to know where, in browser-page pixels,
|
||||
* a Konva node is currently rendered. The math involves three layers:
|
||||
*
|
||||
* 1. Konva internal coordinates (the canonical 300×300 design space)
|
||||
* 2. The Stage container's CSS scale (the design-canvas frame is
|
||||
* transform-scaled to fit the available DOM area)
|
||||
* 3. The Stage container's page-level position (relative to the viewport)
|
||||
*
|
||||
* The two TextElement double-tap handlers (`onDblClick`, `onDblTap`) had
|
||||
* identical 12-line copies of this calculation. Centralizing here makes
|
||||
* the conversion testable and prevents drift between mouse and touch paths.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compute the page-level rectangle of a Konva node, post-CSS-scale.
|
||||
*
|
||||
* Returns `null` if the node has no stage attached (e.g. unmounted) or if
|
||||
* the stage's internal width is 0 (avoids a NaN cssScale). Callers should
|
||||
* treat null as "skip the overlay this time" — the next render will give
|
||||
* them another chance.
|
||||
*
|
||||
* The returned `width`/`height` apply two floors taken from
|
||||
* `constants/elements`:
|
||||
* - width >= MIN_INLINE_EDIT_WIDTH (so an empty textarea is still hittable)
|
||||
* - height >= MIN_INLINE_EDIT_HEIGHT
|
||||
*
|
||||
* `scale` is included so the caller can scale font-size / padding to match
|
||||
* the on-screen rendition (the inline editor multiplies fontSize by scale
|
||||
* so the textarea text aligns with the canvas text it overlays).
|
||||
*
|
||||
* @param {Konva.Node} node - any Konva node with a parent stage
|
||||
* @returns {{ left, top, width, height, scale } | null}
|
||||
*/
|
||||
export function getNodePageRect(node) {
|
||||
if (!node) return null;
|
||||
const stage = typeof node.getStage === 'function' ? node.getStage() : null;
|
||||
if (!stage) return null;
|
||||
const stageWidth = stage.width();
|
||||
if (!(stageWidth > 0)) return null;
|
||||
|
||||
// getClientRect with `relativeTo: stage` returns the node's bounding box
|
||||
// in stage-internal (design) coordinates, accounting for any rotation /
|
||||
// scale on intermediate layers but NOT the Stage's outer CSS transform.
|
||||
const internal = node.getClientRect({ relativeTo: stage });
|
||||
const stageBox = stage.container().getBoundingClientRect();
|
||||
const cssScale = stageBox.width / stageWidth;
|
||||
|
||||
return {
|
||||
left: stageBox.left + internal.x * cssScale,
|
||||
top: stageBox.top + internal.y * cssScale,
|
||||
width: Math.max(MIN_INLINE_EDIT_WIDTH, internal.width * cssScale),
|
||||
height: Math.max(MIN_INLINE_EDIT_HEIGHT, internal.height * cssScale),
|
||||
scale: cssScale,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user