mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 06:30:03 +00:00
fix(studio): resize from the element's real box, not a 200px guess
Resizing an element whose size is driven by a scale animation committed a scale computed against a hardcoded 200px fallback, because the only original size the draft recorded was the element's INLINE width, and a composition sizes its elements from the stylesheet. A 630px chip dropped at 1260px wide committed a scale of 6.3 instead of 2, so it landed at over three times the size it was dropped at. The next drag compounded it, because that wrong scale then counted as the element's live one. The draft now records the box it measured, once, before it writes a width of its own, and the intercept reads that. The inline attributes keep their own job of restoring an inline style, which is why they cannot answer this question.
This commit is contained in:
@@ -12,6 +12,8 @@ import {
|
|||||||
STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR,
|
STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR,
|
||||||
STUDIO_ORIGINAL_WIDTH_ATTR,
|
STUDIO_ORIGINAL_WIDTH_ATTR,
|
||||||
STUDIO_ORIGINAL_HEIGHT_ATTR,
|
STUDIO_ORIGINAL_HEIGHT_ATTR,
|
||||||
|
STUDIO_ORIGINAL_BOX_WIDTH_ATTR,
|
||||||
|
STUDIO_ORIGINAL_BOX_HEIGHT_ATTR,
|
||||||
STUDIO_ORIGINAL_MIN_WIDTH_ATTR,
|
STUDIO_ORIGINAL_MIN_WIDTH_ATTR,
|
||||||
STUDIO_ORIGINAL_MIN_HEIGHT_ATTR,
|
STUDIO_ORIGINAL_MIN_HEIGHT_ATTR,
|
||||||
STUDIO_ORIGINAL_MAX_WIDTH_ATTR,
|
STUDIO_ORIGINAL_MAX_WIDTH_ATTR,
|
||||||
@@ -361,6 +363,12 @@ function writeStudioBoxSizeVars(
|
|||||||
if (!element.hasAttribute(STUDIO_BOX_SIZE_ATTR)) {
|
if (!element.hasAttribute(STUDIO_BOX_SIZE_ATTR)) {
|
||||||
element.setAttribute(STUDIO_ORIGINAL_WIDTH_ATTR, element.style.getPropertyValue("width"));
|
element.setAttribute(STUDIO_ORIGINAL_WIDTH_ATTR, element.style.getPropertyValue("width"));
|
||||||
element.setAttribute(STUDIO_ORIGINAL_HEIGHT_ATTR, element.style.getPropertyValue("height"));
|
element.setAttribute(STUDIO_ORIGINAL_HEIGHT_ATTR, element.style.getPropertyValue("height"));
|
||||||
|
// Measured here and only here: this branch runs once, before the draft
|
||||||
|
// writes a width, so it is the last moment the element still has the box
|
||||||
|
// the user started with. Offset sizes are layout, so a scale animation
|
||||||
|
// running on the element does not distort them.
|
||||||
|
element.setAttribute(STUDIO_ORIGINAL_BOX_WIDTH_ATTR, String(element.offsetWidth));
|
||||||
|
element.setAttribute(STUDIO_ORIGINAL_BOX_HEIGHT_ATTR, String(element.offsetHeight));
|
||||||
element.setAttribute(
|
element.setAttribute(
|
||||||
STUDIO_ORIGINAL_MIN_WIDTH_ATTR,
|
STUDIO_ORIGINAL_MIN_WIDTH_ATTR,
|
||||||
element.style.getPropertyValue("min-width"),
|
element.style.getPropertyValue("min-width"),
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import {
|
|||||||
STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR,
|
STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR,
|
||||||
STUDIO_ORIGINAL_WIDTH_ATTR,
|
STUDIO_ORIGINAL_WIDTH_ATTR,
|
||||||
STUDIO_ORIGINAL_HEIGHT_ATTR,
|
STUDIO_ORIGINAL_HEIGHT_ATTR,
|
||||||
|
STUDIO_ORIGINAL_BOX_WIDTH_ATTR,
|
||||||
|
STUDIO_ORIGINAL_BOX_HEIGHT_ATTR,
|
||||||
STUDIO_ORIGINAL_MIN_WIDTH_ATTR,
|
STUDIO_ORIGINAL_MIN_WIDTH_ATTR,
|
||||||
STUDIO_ORIGINAL_MIN_HEIGHT_ATTR,
|
STUDIO_ORIGINAL_MIN_HEIGHT_ATTR,
|
||||||
STUDIO_ORIGINAL_MAX_WIDTH_ATTR,
|
STUDIO_ORIGINAL_MAX_WIDTH_ATTR,
|
||||||
@@ -222,6 +224,9 @@ describe("buildBoxSizePatches / buildClearBoxSizePatches", () => {
|
|||||||
{ type: "attribute", property: STUDIO_ORIGINAL_WIDTH_ATTR, value: null },
|
{ type: "attribute", property: STUDIO_ORIGINAL_WIDTH_ATTR, value: null },
|
||||||
{ type: "inline-style", property: "height", value: "150px" },
|
{ type: "inline-style", property: "height", value: "150px" },
|
||||||
{ type: "attribute", property: STUDIO_ORIGINAL_HEIGHT_ATTR, value: null },
|
{ type: "attribute", property: STUDIO_ORIGINAL_HEIGHT_ATTR, value: null },
|
||||||
|
// Measurements, so they are cleared without restoring a style.
|
||||||
|
{ type: "attribute", property: STUDIO_ORIGINAL_BOX_WIDTH_ATTR, value: null },
|
||||||
|
{ type: "attribute", property: STUDIO_ORIGINAL_BOX_HEIGHT_ATTR, value: null },
|
||||||
{ type: "inline-style", property: "min-width", value: "0px" },
|
{ type: "inline-style", property: "min-width", value: "0px" },
|
||||||
{ type: "attribute", property: STUDIO_ORIGINAL_MIN_WIDTH_ATTR, value: null },
|
{ type: "attribute", property: STUDIO_ORIGINAL_MIN_WIDTH_ATTR, value: null },
|
||||||
{ type: "inline-style", property: "min-height", value: "0px" },
|
{ type: "inline-style", property: "min-height", value: "0px" },
|
||||||
@@ -257,8 +262,8 @@ describe("buildBoxSizePatches / buildClearBoxSizePatches", () => {
|
|||||||
|
|
||||||
it("clear: bare element emits only null ops — no style restores fire when orig attrs are absent", () => {
|
it("clear: bare element emits only null ops — no style restores fire when orig attrs are absent", () => {
|
||||||
const ops = buildClearBoxSizePatches(div());
|
const ops = buildClearBoxSizePatches(div());
|
||||||
// 3 fixed (studio-width, studio-height, box-size marker) + 14 attr-null pushes (one per BOX_SIZE_ORIG_ATTR)
|
// 3 fixed (studio-width, studio-height, box-size marker) + 16 attr-null pushes (one per BOX_SIZE_ORIG_ATTR)
|
||||||
expect(ops).toHaveLength(17);
|
expect(ops).toHaveLength(19);
|
||||||
expect(ops.every((op) => op.value === null)).toBe(true);
|
expect(ops.every((op) => op.value === null)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import {
|
|||||||
STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR,
|
STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR,
|
||||||
STUDIO_ORIGINAL_WIDTH_ATTR,
|
STUDIO_ORIGINAL_WIDTH_ATTR,
|
||||||
STUDIO_ORIGINAL_HEIGHT_ATTR,
|
STUDIO_ORIGINAL_HEIGHT_ATTR,
|
||||||
|
STUDIO_ORIGINAL_BOX_WIDTH_ATTR,
|
||||||
|
STUDIO_ORIGINAL_BOX_HEIGHT_ATTR,
|
||||||
STUDIO_ORIGINAL_MIN_WIDTH_ATTR,
|
STUDIO_ORIGINAL_MIN_WIDTH_ATTR,
|
||||||
STUDIO_ORIGINAL_MIN_HEIGHT_ATTR,
|
STUDIO_ORIGINAL_MIN_HEIGHT_ATTR,
|
||||||
STUDIO_ORIGINAL_MAX_WIDTH_ATTR,
|
STUDIO_ORIGINAL_MAX_WIDTH_ATTR,
|
||||||
@@ -135,6 +137,9 @@ const BOX_SIZE_STYLE_PROPS = [
|
|||||||
const BOX_SIZE_ORIG_ATTRS: ReadonlyArray<[string, string]> = [
|
const BOX_SIZE_ORIG_ATTRS: ReadonlyArray<[string, string]> = [
|
||||||
[STUDIO_ORIGINAL_WIDTH_ATTR, "width"],
|
[STUDIO_ORIGINAL_WIDTH_ATTR, "width"],
|
||||||
[STUDIO_ORIGINAL_HEIGHT_ATTR, "height"],
|
[STUDIO_ORIGINAL_HEIGHT_ATTR, "height"],
|
||||||
|
// Records a measurement rather than a style, so it restores nothing.
|
||||||
|
[STUDIO_ORIGINAL_BOX_WIDTH_ATTR, ""],
|
||||||
|
[STUDIO_ORIGINAL_BOX_HEIGHT_ATTR, ""],
|
||||||
[STUDIO_ORIGINAL_MIN_WIDTH_ATTR, "min-width"],
|
[STUDIO_ORIGINAL_MIN_WIDTH_ATTR, "min-width"],
|
||||||
[STUDIO_ORIGINAL_MIN_HEIGHT_ATTR, "min-height"],
|
[STUDIO_ORIGINAL_MIN_HEIGHT_ATTR, "min-height"],
|
||||||
[STUDIO_ORIGINAL_MAX_WIDTH_ATTR, "max-width"],
|
[STUDIO_ORIGINAL_MAX_WIDTH_ATTR, "max-width"],
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import {
|
|||||||
STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR,
|
STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR,
|
||||||
STUDIO_ORIGINAL_WIDTH_ATTR,
|
STUDIO_ORIGINAL_WIDTH_ATTR,
|
||||||
STUDIO_ORIGINAL_HEIGHT_ATTR,
|
STUDIO_ORIGINAL_HEIGHT_ATTR,
|
||||||
|
STUDIO_ORIGINAL_BOX_WIDTH_ATTR,
|
||||||
|
STUDIO_ORIGINAL_BOX_HEIGHT_ATTR,
|
||||||
STUDIO_ORIGINAL_MIN_WIDTH_ATTR,
|
STUDIO_ORIGINAL_MIN_WIDTH_ATTR,
|
||||||
STUDIO_ORIGINAL_MIN_HEIGHT_ATTR,
|
STUDIO_ORIGINAL_MIN_HEIGHT_ATTR,
|
||||||
STUDIO_ORIGINAL_MAX_WIDTH_ATTR,
|
STUDIO_ORIGINAL_MAX_WIDTH_ATTR,
|
||||||
@@ -60,6 +62,8 @@ export function captureStudioBoxSize(element: HTMLElement): StudioBoxSizeSnapsho
|
|||||||
marker: element.getAttribute(STUDIO_BOX_SIZE_ATTR),
|
marker: element.getAttribute(STUDIO_BOX_SIZE_ATTR),
|
||||||
originalWidth: element.getAttribute(STUDIO_ORIGINAL_WIDTH_ATTR),
|
originalWidth: element.getAttribute(STUDIO_ORIGINAL_WIDTH_ATTR),
|
||||||
originalHeight: element.getAttribute(STUDIO_ORIGINAL_HEIGHT_ATTR),
|
originalHeight: element.getAttribute(STUDIO_ORIGINAL_HEIGHT_ATTR),
|
||||||
|
originalBoxWidth: element.getAttribute(STUDIO_ORIGINAL_BOX_WIDTH_ATTR),
|
||||||
|
originalBoxHeight: element.getAttribute(STUDIO_ORIGINAL_BOX_HEIGHT_ATTR),
|
||||||
originalMinWidth: element.getAttribute(STUDIO_ORIGINAL_MIN_WIDTH_ATTR),
|
originalMinWidth: element.getAttribute(STUDIO_ORIGINAL_MIN_WIDTH_ATTR),
|
||||||
originalMinHeight: element.getAttribute(STUDIO_ORIGINAL_MIN_HEIGHT_ATTR),
|
originalMinHeight: element.getAttribute(STUDIO_ORIGINAL_MIN_HEIGHT_ATTR),
|
||||||
originalMaxWidth: element.getAttribute(STUDIO_ORIGINAL_MAX_WIDTH_ATTR),
|
originalMaxWidth: element.getAttribute(STUDIO_ORIGINAL_MAX_WIDTH_ATTR),
|
||||||
@@ -128,6 +132,8 @@ export function restoreStudioBoxSize(element: HTMLElement, previous: StudioBoxSi
|
|||||||
restoreAttribute(element, STUDIO_BOX_SIZE_ATTR, previous.marker);
|
restoreAttribute(element, STUDIO_BOX_SIZE_ATTR, previous.marker);
|
||||||
restoreAttribute(element, STUDIO_ORIGINAL_WIDTH_ATTR, previous.originalWidth);
|
restoreAttribute(element, STUDIO_ORIGINAL_WIDTH_ATTR, previous.originalWidth);
|
||||||
restoreAttribute(element, STUDIO_ORIGINAL_HEIGHT_ATTR, previous.originalHeight);
|
restoreAttribute(element, STUDIO_ORIGINAL_HEIGHT_ATTR, previous.originalHeight);
|
||||||
|
restoreAttribute(element, STUDIO_ORIGINAL_BOX_WIDTH_ATTR, previous.originalBoxWidth);
|
||||||
|
restoreAttribute(element, STUDIO_ORIGINAL_BOX_HEIGHT_ATTR, previous.originalBoxHeight);
|
||||||
restoreAttribute(element, STUDIO_ORIGINAL_MIN_WIDTH_ATTR, previous.originalMinWidth);
|
restoreAttribute(element, STUDIO_ORIGINAL_MIN_WIDTH_ATTR, previous.originalMinWidth);
|
||||||
restoreAttribute(element, STUDIO_ORIGINAL_MIN_HEIGHT_ATTR, previous.originalMinHeight);
|
restoreAttribute(element, STUDIO_ORIGINAL_MIN_HEIGHT_ATTR, previous.originalMinHeight);
|
||||||
restoreAttribute(element, STUDIO_ORIGINAL_MAX_WIDTH_ATTR, previous.originalMaxWidth);
|
restoreAttribute(element, STUDIO_ORIGINAL_MAX_WIDTH_ATTR, previous.originalMaxWidth);
|
||||||
|
|||||||
@@ -16,6 +16,18 @@ export const STUDIO_ORIGINAL_TRANSLATE_ATTR = "data-hf-studio-original-translate
|
|||||||
export const STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR = "data-hf-studio-original-inline-translate";
|
export const STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR = "data-hf-studio-original-inline-translate";
|
||||||
export const STUDIO_ORIGINAL_WIDTH_ATTR = "data-hf-studio-original-width";
|
export const STUDIO_ORIGINAL_WIDTH_ATTR = "data-hf-studio-original-width";
|
||||||
export const STUDIO_ORIGINAL_HEIGHT_ATTR = "data-hf-studio-original-height";
|
export const STUDIO_ORIGINAL_HEIGHT_ATTR = "data-hf-studio-original-height";
|
||||||
|
/**
|
||||||
|
* The element's laid-out box before a resize draft touched it, in CSS pixels.
|
||||||
|
*
|
||||||
|
* The two attributes above record the element's INLINE width and height so a
|
||||||
|
* reset can put them back, and are empty for the usual case of an element sized
|
||||||
|
* by the stylesheet. That made them useless as a measurement, and the resize
|
||||||
|
* intercept, which needs the original box to work out a scale, fell back to a
|
||||||
|
* hardcoded guess and produced a wildly wrong one. These record the measurement
|
||||||
|
* instead, and restore nothing.
|
||||||
|
*/
|
||||||
|
export const STUDIO_ORIGINAL_BOX_WIDTH_ATTR = "data-hf-studio-original-box-width";
|
||||||
|
export const STUDIO_ORIGINAL_BOX_HEIGHT_ATTR = "data-hf-studio-original-box-height";
|
||||||
export const STUDIO_ORIGINAL_MIN_WIDTH_ATTR = "data-hf-studio-original-min-width";
|
export const STUDIO_ORIGINAL_MIN_WIDTH_ATTR = "data-hf-studio-original-min-width";
|
||||||
export const STUDIO_ORIGINAL_MIN_HEIGHT_ATTR = "data-hf-studio-original-min-height";
|
export const STUDIO_ORIGINAL_MIN_HEIGHT_ATTR = "data-hf-studio-original-min-height";
|
||||||
export const STUDIO_ORIGINAL_MAX_WIDTH_ATTR = "data-hf-studio-original-max-width";
|
export const STUDIO_ORIGINAL_MAX_WIDTH_ATTR = "data-hf-studio-original-max-width";
|
||||||
@@ -70,6 +82,8 @@ export interface StudioBoxSizeSnapshot {
|
|||||||
marker: string | null;
|
marker: string | null;
|
||||||
originalWidth: string | null;
|
originalWidth: string | null;
|
||||||
originalHeight: string | null;
|
originalHeight: string | null;
|
||||||
|
originalBoxWidth: string | null;
|
||||||
|
originalBoxHeight: string | null;
|
||||||
originalMinWidth: string | null;
|
originalMinWidth: string | null;
|
||||||
originalMinHeight: string | null;
|
originalMinHeight: string | null;
|
||||||
originalMaxWidth: string | null;
|
originalMaxWidth: string | null;
|
||||||
|
|||||||
@@ -284,3 +284,50 @@ it("non-uniform drag commits scaleX/scaleY longhands", async () => {
|
|||||||
expect(serialized).toContain("scaleX");
|
expect(serialized).toContain("scaleX");
|
||||||
expect(serialized).toContain("scaleY");
|
expect(serialized).toContain("scaleY");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bug: the original box size was read only from the element's INLINE
|
||||||
|
* width, and a composition sizes its elements from the stylesheet. With no
|
||||||
|
* inline width the code fell back to a hardcoded 200, so the committed scale
|
||||||
|
* came out `real / 200` times too large. Dropping a 630px chip at 2391px wide
|
||||||
|
* left it rendering at 7532px, over three times where it was dropped, and the
|
||||||
|
* next drag compounded it.
|
||||||
|
*/
|
||||||
|
it("scales from the element's real box, not a hardcoded fallback", async () => {
|
||||||
|
const el = document.createElement("div");
|
||||||
|
el.id = "clip";
|
||||||
|
// Sized by a stylesheet, so it carries no inline width, and the draft
|
||||||
|
// recorded the box it measured instead.
|
||||||
|
el.setAttribute("data-hf-studio-original-box-width", "630");
|
||||||
|
el.setAttribute("data-hf-studio-original-box-height", "252");
|
||||||
|
document.body.append(el);
|
||||||
|
const selection = { id: "clip", selector: "#clip", element: el } as DomEditSelection;
|
||||||
|
const commitMutation = vi.fn();
|
||||||
|
|
||||||
|
await tryGsapResizeIntercept(
|
||||||
|
selection,
|
||||||
|
{ width: 1260, height: 504 },
|
||||||
|
[keyframedScaleFixture()],
|
||||||
|
fakeIframe(el, { scaleX: 1, scaleY: 1 }),
|
||||||
|
commitMutation,
|
||||||
|
);
|
||||||
|
|
||||||
|
type Mutation = {
|
||||||
|
properties?: Record<string, number>;
|
||||||
|
keyframes?: Array<{ percentage: number; properties: Record<string, number> }>;
|
||||||
|
};
|
||||||
|
const committed = commitMutation.mock.calls
|
||||||
|
.map((call) => call[1] as Mutation)
|
||||||
|
.flatMap((mutation) => [
|
||||||
|
mutation.properties,
|
||||||
|
...(mutation.keyframes ?? []).map((frame) => frame.properties),
|
||||||
|
])
|
||||||
|
.filter((properties): properties is Record<string, number> => properties != null)
|
||||||
|
.find((properties) => properties.scale != null || properties.scaleX != null);
|
||||||
|
|
||||||
|
// Dropped at twice the element's own size, so the scale is about 2. The
|
||||||
|
// number that matters is that it is not the 6.3 which 1260/200 produced.
|
||||||
|
const scale = committed?.scale ?? committed?.scaleX ?? 0;
|
||||||
|
expect(scale).toBeCloseTo(2, 1);
|
||||||
|
expect(scale).toBeLessThan(3);
|
||||||
|
});
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
import type { GsapAnimation, PropertyGroupName } from "@hyperframes/core/gsap-parser";
|
import type { GsapAnimation, PropertyGroupName } from "@hyperframes/core/gsap-parser";
|
||||||
import type { DomEditSelection } from "../components/editor/domEditingTypes";
|
import type { DomEditSelection } from "../components/editor/domEditingTypes";
|
||||||
import { clearStudioBoxSize } from "../components/editor/manualEdits";
|
import { clearStudioBoxSize } from "../components/editor/manualEdits";
|
||||||
|
import {
|
||||||
|
STUDIO_ORIGINAL_BOX_HEIGHT_ATTR,
|
||||||
|
STUDIO_ORIGINAL_BOX_WIDTH_ATTR,
|
||||||
|
} from "../components/editor/manualEditsTypes";
|
||||||
import { setElementGsapPosition } from "../utils/elementGsap";
|
import { setElementGsapPosition } from "../utils/elementGsap";
|
||||||
import { usePlayerStore } from "../player/store/playerStore";
|
import { usePlayerStore } from "../player/store/playerStore";
|
||||||
import { readAllAnimatedProperties, readGsapProperty } from "./gsapRuntimeReaders";
|
import { readAllAnimatedProperties, readGsapProperty } from "./gsapRuntimeReaders";
|
||||||
@@ -49,6 +53,26 @@ function synthesizeIdentityProps(
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The element's box before the resize draft ran, in CSS pixels.
|
||||||
|
*
|
||||||
|
* Prefers the measurement the draft recorded. Falls back to the inline style it
|
||||||
|
* saved for restoring, which is a real value for the elements that carry one,
|
||||||
|
* and null when neither says anything.
|
||||||
|
*/
|
||||||
|
function originalBoxSize(
|
||||||
|
el: HTMLElement | null,
|
||||||
|
measuredAttr: string,
|
||||||
|
inlineProperty: "width" | "height",
|
||||||
|
): number | null {
|
||||||
|
const measured = Number.parseFloat(el?.getAttribute(measuredAttr) ?? "");
|
||||||
|
if (Number.isFinite(measured) && measured > 0) return measured;
|
||||||
|
const inline = Number.parseFloat(
|
||||||
|
el?.getAttribute(`data-hf-studio-original-${inlineProperty}`) ?? "",
|
||||||
|
);
|
||||||
|
return Number.isFinite(inline) && inline > 0 ? inline : null;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Resize intercept ──────────────────────────────────────────────────────
|
// ── Resize intercept ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
// fallow-ignore-next-line complexity
|
// fallow-ignore-next-line complexity
|
||||||
@@ -162,10 +186,15 @@ export async function tryGsapResizeIntercept(
|
|||||||
const el = iframe?.contentDocument?.querySelector(selector ?? "") as HTMLElement | null;
|
const el = iframe?.contentDocument?.querySelector(selector ?? "") as HTMLElement | null;
|
||||||
// The resize draft modifies el.style.width/height, so read the ORIGINAL
|
// The resize draft modifies el.style.width/height, so read the ORIGINAL
|
||||||
// dimensions saved by the draft system before it ran.
|
// dimensions saved by the draft system before it ran.
|
||||||
const origW = Number.parseFloat(el?.getAttribute("data-hf-studio-original-width") ?? "");
|
//
|
||||||
const origH = Number.parseFloat(el?.getAttribute("data-hf-studio-original-height") ?? "");
|
// The measured box first, then the inline one. The inline attributes exist
|
||||||
const cssW = Number.isFinite(origW) && origW > 0 ? origW : 200;
|
// to restore an inline style and are empty for anything sized by a
|
||||||
const cssH = Number.isFinite(origH) && origH > 0 ? origH : cssW;
|
// stylesheet, which is how compositions are written, so reading them alone
|
||||||
|
// sent almost every element to the fallback below: a 630px chip scaled by
|
||||||
|
// 630/200, landing over three times the size it was dropped at, and worse
|
||||||
|
// on the next drag because the wrong scale then counted as its live one.
|
||||||
|
const cssW = originalBoxSize(el, STUDIO_ORIGINAL_BOX_WIDTH_ATTR, "width") ?? 200;
|
||||||
|
const cssH = originalBoxSize(el, STUDIO_ORIGINAL_BOX_HEIGHT_ATTR, "height") ?? cssW;
|
||||||
// `size` is the draft's CSS box; on screen it is multiplied by the element's
|
// `size` is the draft's CSS box; on screen it is multiplied by the element's
|
||||||
// LIVE scale (the draft divides the cursor delta by it — see
|
// LIVE scale (the draft divides the cursor delta by it — see
|
||||||
// resolveDomEditResizeGesture). The committed keyframe REPLACES that live
|
// resolveDomEditResizeGesture). The committed keyframe REPLACES that live
|
||||||
|
|||||||
Reference in New Issue
Block a user