Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 30 additions & 23 deletions packages/core/src/parsers/__goldens__/complex.parsed.json
Original file line number Diff line number Diff line change
@@ -1,55 +1,62 @@
{
"animations": [
{
"targetSelector": ".ambient-line",
"targetSelector": ".headline span",
"method": "from",
"position": 0.16,
"position": 0.05,
"properties": {
"scaleX": 0,
"y": 46,
"opacity": 0
},
"duration": 0.42,
"duration": 0.38,
"ease": "back.out(1.35)",
"extras": {
"stagger": "__raw:0.08"
"stagger": "__raw:0.055"
},
"id": ".ambient-line-from-160"
"resolvedStart": 0.05,
"id": ".headline span-from-50"
},
{
"targetSelector": ".ambient-word",
"targetSelector": ".headline .sub",
"method": "from",
"position": 0.08,
"position": 0.2,
"properties": {
"scale": 0.92,
"y": 20,
"opacity": 0
},
"duration": 0.5,
"id": ".ambient-word-from-80"
"duration": 0.28,
"ease": "power3.out",
"resolvedStart": 0.2,
"id": ".headline .sub-from-200"
},
{
"targetSelector": ".headline .sub",
"targetSelector": ".ambient-word",
"method": "from",
"position": 0.2,
"position": 0.08,
"properties": {
"y": 20,
"scale": 0.92,
"opacity": 0
},
"duration": 0.28,
"id": ".headline .sub-from-200"
"duration": 0.5,
"ease": "power3.out",
"resolvedStart": 0.08,
"id": ".ambient-word-from-80"
},
{
"targetSelector": ".headline span",
"targetSelector": ".ambient-line",
"method": "from",
"position": 0.05,
"position": 0.16,
"properties": {
"y": 46,
"scaleX": 0,
"opacity": 0
},
"duration": 0.38,
"ease": "back.out(1.35)",
"duration": 0.42,
"ease": "power3.out",
"extras": {
"stagger": "__raw:0.055"
"stagger": "__raw:0.08"
},
"id": ".headline span-from-50"
"resolvedStart": 0.16,
"id": ".ambient-line-from-160"
}
],
"timelineVar": "tl",
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/parsers/__goldens__/complex.serialized.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
gsap.defaults({ force3D: true });
const tl = gsap.timeline({ paused: true, defaults: { duration: 0.45, ease: "power3.out" } });
tl.from(".headline span", { y: 46, opacity: 0, duration: 0.38, ease: "back.out(1.35)", stagger: 0.055 }, 0.05);
tl.from(".ambient-word", { scale: 0.92, opacity: 0, duration: 0.5 }, 0.08);
tl.from(".ambient-line", { scaleX: 0, opacity: 0, duration: 0.42, stagger: 0.08 }, 0.16);
tl.from(".headline .sub", { y: 20, opacity: 0, duration: 0.28 }, 0.2);
tl.from(".ambient-word", { scale: 0.92, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.08);
tl.from(".ambient-line", { scaleX: 0, opacity: 0, duration: 0.42, ease: "power3.out", stagger: 0.08 }, 0.16);
tl.from(".headline .sub", { y: 20, opacity: 0, duration: 0.28, ease: "power3.out" }, 0.2);
window.__timelines["vpn-youtube-spot"] = tl;

2 changes: 2 additions & 0 deletions packages/core/src/parsers/__goldens__/fromto.parsed.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
},
"duration": 0.6,
"ease": "power3.out",
"resolvedStart": 0.1,
"id": "#hero-fromTo-100"
},
{
Expand All @@ -29,6 +30,7 @@
"opacity": 0
},
"duration": 0.45,
"resolvedStart": 0.5,
"id": "#caption-fromTo-500"
}
],
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/parsers/__goldens__/minimal.parsed.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"duration": 0.5,
"ease": "power3.out",
"resolvedStart": 0.2,
"id": "#notification-to-200"
},
{
Expand All @@ -22,6 +23,7 @@
},
"duration": 0.3,
"ease": "power3.in",
"resolvedStart": 4.2,
"id": "#notification-to-4200"
}
],
Expand Down
18 changes: 14 additions & 4 deletions packages/core/src/parsers/__goldens__/moderate.parsed.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"duration": 0.5,
"ease": "power3.out",
"resolvedStart": 0.1,
"id": "#card-to-100"
},
{
Expand All @@ -21,7 +22,9 @@
},
"duration": 0.15,
"ease": "power2.out",
"id": "#subscribe-btn-to-1000"
"propertyGroup": "scale",
"resolvedStart": 1,
"id": "#subscribe-btn-to-1000-scale"
},
{
"targetSelector": "#subscribe-btn",
Expand All @@ -32,7 +35,9 @@
},
"duration": 0.4,
"ease": "elastic.out(1, 0.4)",
"id": "#subscribe-btn-to-1150"
"propertyGroup": "scale",
"resolvedStart": 1.15,
"id": "#subscribe-btn-to-1150-scale"
},
{
"targetSelector": "#btn-subscribe",
Expand All @@ -43,7 +48,9 @@
},
"duration": 0.08,
"ease": "none",
"id": "#btn-subscribe-to-1150"
"propertyGroup": "visual",
"resolvedStart": 1.15,
"id": "#btn-subscribe-to-1150-visual"
},
{
"targetSelector": "#btn-subscribed",
Expand All @@ -54,7 +61,9 @@
},
"duration": 0.08,
"ease": "none",
"id": "#btn-subscribed-to-1180"
"propertyGroup": "visual",
"resolvedStart": 1.18,
"id": "#btn-subscribed-to-1180-visual"
},
{
"targetSelector": "#card",
Expand All @@ -66,6 +75,7 @@
},
"duration": 0.25,
"ease": "power3.in",
"resolvedStart": 3.8,
"id": "#card-to-3800"
}
],
Expand Down
40 changes: 40 additions & 0 deletions packages/core/src/parsers/gsapConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,46 @@ export const SUPPORTED_PROPS = [
"innerText",
];

// ── Property Groups ─────────────────────────────────────────────────────────
// Each group maps to an independent GSAP tween so editing one property
// (e.g. drag → x/y) never contaminates another (e.g. scale, rotation).

export type PropertyGroupName = "position" | "scale" | "size" | "rotation" | "visual" | "other";

export const PROPERTY_GROUPS: Record<PropertyGroupName, ReadonlySet<string>> = {
position: new Set(["x", "y", "xPercent", "yPercent"]),
scale: new Set(["scale", "scaleX", "scaleY"]),
size: new Set(["width", "height"]),
rotation: new Set(["rotation", "skewX", "skewY"]),
visual: new Set(["opacity", "autoAlpha"]),
other: new Set<string>(),
};

const PROP_TO_GROUP = new Map<string, PropertyGroupName>();
for (const [group, props] of Object.entries(PROPERTY_GROUPS) as [
PropertyGroupName,
ReadonlySet<string>,
][]) {
for (const p of props) PROP_TO_GROUP.set(p, group);
}

export function classifyPropertyGroup(prop: string): PropertyGroupName {
return PROP_TO_GROUP.get(prop) ?? "other";
}

export function classifyTweenPropertyGroup(
properties: Record<string, unknown>,
): PropertyGroupName | undefined {
const groups = new Set<PropertyGroupName>();
for (const key of Object.keys(properties)) {
if (key === "transformOrigin") continue;
const g = classifyPropertyGroup(key);
groups.add(g);
}
if (groups.size === 1) return groups.values().next().value;
return undefined;
}

export const SUPPORTED_EASES = [
"none",
"power1.in",
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/parsers/gsapParser.stress.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,9 +658,9 @@ describe("14. ID collision", () => {
const ids = result.animations.map((a) => a.id);
// All IDs must be unique
expect(new Set(ids).size).toBe(3);
expect(ids[0]).toBe("#el-to-0");
expect(ids[1]).toBe("#el-to-0-2");
expect(ids[2]).toBe("#el-to-0-3");
expect(ids[0]).toBe("#el-to-0-visual");
expect(ids[1]).toBe("#el-to-0-position");
expect(ids[2]).toBe("#el-to-0-position-2");
});

it("disambiguated IDs are stable across parses", () => {
Expand Down Expand Up @@ -932,7 +932,7 @@ describe("Additional edge cases", () => {
`;
const result = parseGsapScript(script);
// ID uses Math.round(position * 1000) for numeric positions
expect(result.animations[0].id).toBe("#el-to--2500");
expect(result.animations[0].id).toBe("#el-to--2500-position");
});

it("fromTo with no position arg defaults to 0", () => {
Expand Down
Loading
Loading