@hyperframes/core 0.6.85 → 0.6.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/runtime-inline.js +1 -1
- package/dist/generated/runtime-inline.js.map +1 -1
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +19 -19
- package/dist/hyperframe.runtime.mjs +19 -19
- package/dist/parsers/gsapConstants.d.ts.map +1 -1
- package/dist/parsers/gsapConstants.js +1 -0
- package/dist/parsers/gsapConstants.js.map +1 -1
- package/dist/parsers/gsapParser.d.ts +14 -2
- package/dist/parsers/gsapParser.d.ts.map +1 -1
- package/dist/parsers/gsapParser.js +372 -6
- package/dist/parsers/gsapParser.js.map +1 -1
- package/dist/parsers/gsapSerialize.d.ts +18 -0
- package/dist/parsers/gsapSerialize.d.ts.map +1 -1
- package/dist/parsers/gsapSerialize.js +0 -2
- package/dist/parsers/gsapSerialize.js.map +1 -1
- package/dist/studio-api/helpers/sourceMutation.d.ts.map +1 -1
- package/dist/studio-api/helpers/sourceMutation.js +30 -7
- package/dist/studio-api/helpers/sourceMutation.js.map +1 -1
- package/dist/studio-api/routes/files.d.ts.map +1 -1
- package/dist/studio-api/routes/files.js +29 -0
- package/dist/studio-api/routes/files.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gsapConstants.d.ts","sourceRoot":"","sources":["../../src/parsers/gsapConstants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,eAAe,UAsC3B,CAAC;AAEF,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"gsapConstants.d.ts","sourceRoot":"","sources":["../../src/parsers/gsapConstants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,eAAe,UAsC3B,CAAC;AAEF,eAAO,MAAM,eAAe,UAgC3B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gsapConstants.js","sourceRoot":"","sources":["../../src/parsers/gsapConstants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,gBAAgB;IAChB,GAAG;IACH,GAAG;IACH,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,OAAO;IACP,OAAO;IACP,gBAAgB;IAChB,GAAG;IACH,WAAW;IACX,WAAW;IACX,WAAW;IACX,aAAa;IACb,iBAAiB;IACjB,aAAa;IACb,SAAS;IACT,YAAY;IACZ,WAAW;IACX,aAAa;IACb,OAAO;IACP,QAAQ;IACR,SAAS;IACT,OAAO;IACP,iBAAiB;IACjB,aAAa;IACb,YAAY;IACZ,cAAc;IACd,aAAa;IACb,UAAU;IACV,eAAe;IACf,oBAAoB;IACpB,QAAQ;IACR,UAAU;IACV,+CAA+C;IAC/C,WAAW;CACZ,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,MAAM;IACN,WAAW;IACX,YAAY;IACZ,cAAc;IACd,WAAW;IACX,YAAY;IACZ,cAAc;IACd,WAAW;IACX,YAAY;IACZ,cAAc;IACd,WAAW;IACX,YAAY;IACZ,cAAc;IACd,SAAS;IACT,UAAU;IACV,YAAY;IACZ,YAAY;IACZ,aAAa;IACb,eAAe;IACf,WAAW;IACX,YAAY;IACZ,cAAc;IACd,SAAS;IACT,UAAU;IACV,YAAY;IACZ,eAAe;IACf,eAAe;IACf,cAAc;IACd,eAAe;IACf,cAAc;
|
|
1
|
+
{"version":3,"file":"gsapConstants.js","sourceRoot":"","sources":["../../src/parsers/gsapConstants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,gBAAgB;IAChB,GAAG;IACH,GAAG;IACH,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,OAAO;IACP,OAAO;IACP,gBAAgB;IAChB,GAAG;IACH,WAAW;IACX,WAAW;IACX,WAAW;IACX,aAAa;IACb,iBAAiB;IACjB,aAAa;IACb,SAAS;IACT,YAAY;IACZ,WAAW;IACX,aAAa;IACb,OAAO;IACP,QAAQ;IACR,SAAS;IACT,OAAO;IACP,iBAAiB;IACjB,aAAa;IACb,YAAY;IACZ,cAAc;IACd,aAAa;IACb,UAAU;IACV,eAAe;IACf,oBAAoB;IACpB,QAAQ;IACR,UAAU;IACV,+CAA+C;IAC/C,WAAW;CACZ,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,MAAM;IACN,WAAW;IACX,YAAY;IACZ,cAAc;IACd,WAAW;IACX,YAAY;IACZ,cAAc;IACd,WAAW;IACX,YAAY;IACZ,cAAc;IACd,WAAW;IACX,YAAY;IACZ,cAAc;IACd,SAAS;IACT,UAAU;IACV,YAAY;IACZ,YAAY;IACZ,aAAa;IACb,eAAe;IACf,WAAW;IACX,YAAY;IACZ,cAAc;IACd,SAAS;IACT,UAAU;IACV,YAAY;IACZ,eAAe;IACf,eAAe;IACf,cAAc;IACd,eAAe;IACf,cAAc;IACd,UAAU;CACX,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type GsapAnimation, type ParsedGsap } from "./gsapSerialize";
|
|
2
|
-
export type { GsapAnimation, GsapMethod, ParsedGsap, GsapKeyframesData, GsapPercentageKeyframe, GsapKeyframeFormat, } from "./gsapSerialize";
|
|
1
|
+
import { type ArcPathConfig, type ArcPathSegment, type GsapAnimation, type ParsedGsap } from "./gsapSerialize";
|
|
2
|
+
export type { ArcPathConfig, ArcPathSegment, GsapAnimation, GsapMethod, ParsedGsap, GsapKeyframesData, GsapPercentageKeyframe, GsapKeyframeFormat, } from "./gsapSerialize";
|
|
3
3
|
export { serializeGsapAnimations, getAnimationsForElementId, validateCompositionGsap, keyframesToGsapAnimations, gsapAnimationsToKeyframes, SUPPORTED_PROPS, SUPPORTED_EASES, } from "./gsapSerialize";
|
|
4
4
|
export { generateSpringEaseData, SPRING_PRESETS } from "./springEase";
|
|
5
5
|
export type { SpringPreset } from "./springEase";
|
|
@@ -9,6 +9,15 @@ export declare function addAnimationToScript(script: string, animation: Omit<Gsa
|
|
|
9
9
|
script: string;
|
|
10
10
|
id: string;
|
|
11
11
|
};
|
|
12
|
+
export declare function addAnimationWithKeyframesToScript(script: string, targetSelector: string, position: number, duration: number, keyframes: Array<{
|
|
13
|
+
percentage: number;
|
|
14
|
+
properties: Record<string, number | string>;
|
|
15
|
+
ease?: string;
|
|
16
|
+
auto?: boolean;
|
|
17
|
+
}>, ease?: string): {
|
|
18
|
+
script: string;
|
|
19
|
+
id: string;
|
|
20
|
+
};
|
|
12
21
|
export declare function removeAnimationFromScript(script: string, animationId: string): string;
|
|
13
22
|
/**
|
|
14
23
|
* Insert a keyframe at the given percentage in an existing percentage-keyframes
|
|
@@ -45,6 +54,9 @@ export declare function materializeKeyframesInScript(script: string, animationId
|
|
|
45
54
|
properties: Record<string, number | string>;
|
|
46
55
|
ease?: string;
|
|
47
56
|
}>, easeEach?: string, resolvedSelector?: string): string;
|
|
57
|
+
export declare function setArcPathInScript(script: string, animationId: string, config: ArcPathConfig): string;
|
|
58
|
+
export declare function updateArcSegmentInScript(script: string, animationId: string, segmentIndex: number, update: Partial<ArcPathSegment>): string;
|
|
59
|
+
export declare function removeArcPathFromScript(script: string, animationId: string): string;
|
|
48
60
|
/**
|
|
49
61
|
* Replace a dynamic loop that generates multiple tween calls with individual
|
|
50
62
|
* static `tl.to()` calls — one per element. Finds the loop containing the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gsapParser.d.ts","sourceRoot":"","sources":["../../src/parsers/gsapParser.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,aAAa,EAIlB,KAAK,UAAU,EAChB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,aAAa,EACb,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,uBAAuB,EACvB,yBAAyB,EACzB,uBAAuB,EACvB,yBAAyB,EACzB,yBAAyB,EACzB,eAAe,EACf,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACtE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"gsapParser.d.ts","sourceRoot":"","sources":["../../src/parsers/gsapParser.ts"],"names":[],"mappings":"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"}
|
|
@@ -587,6 +587,81 @@ function parseSimpleArrayKeyframes(node, scope) {
|
|
|
587
587
|
...(easeEach ? { easeEach } : {}),
|
|
588
588
|
};
|
|
589
589
|
}
|
|
590
|
+
function parseMotionPathNode(node, scope) {
|
|
591
|
+
if (!node)
|
|
592
|
+
return undefined;
|
|
593
|
+
let pathNode;
|
|
594
|
+
let autoRotate = false;
|
|
595
|
+
let curviness = 1;
|
|
596
|
+
let isCubic = false;
|
|
597
|
+
if (node.type === "ObjectExpression") {
|
|
598
|
+
for (const prop of node.properties ?? []) {
|
|
599
|
+
if (!isObjectProperty(prop))
|
|
600
|
+
continue;
|
|
601
|
+
const key = propKeyName(prop);
|
|
602
|
+
if (key === "path")
|
|
603
|
+
pathNode = prop.value;
|
|
604
|
+
else if (key === "autoRotate") {
|
|
605
|
+
const val = resolveNode(prop.value, scope);
|
|
606
|
+
autoRotate = typeof val === "number" ? val : val === true;
|
|
607
|
+
}
|
|
608
|
+
else if (key === "curviness") {
|
|
609
|
+
const val = resolveNode(prop.value, scope);
|
|
610
|
+
if (typeof val === "number")
|
|
611
|
+
curviness = val;
|
|
612
|
+
}
|
|
613
|
+
else if (key === "type") {
|
|
614
|
+
const val = resolveNode(prop.value, scope);
|
|
615
|
+
if (val === "cubic")
|
|
616
|
+
isCubic = true;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
else if (node.type === "ArrayExpression") {
|
|
621
|
+
pathNode = node;
|
|
622
|
+
}
|
|
623
|
+
if (!pathNode || pathNode.type !== "ArrayExpression")
|
|
624
|
+
return undefined;
|
|
625
|
+
const elements = pathNode.elements ?? [];
|
|
626
|
+
const coords = [];
|
|
627
|
+
for (const elem of elements) {
|
|
628
|
+
if (!elem || elem.type !== "ObjectExpression")
|
|
629
|
+
continue;
|
|
630
|
+
const rec = objectExpressionToRecord(elem, scope);
|
|
631
|
+
const x = typeof rec.x === "number" ? rec.x : undefined;
|
|
632
|
+
const y = typeof rec.y === "number" ? rec.y : undefined;
|
|
633
|
+
if (x !== undefined && y !== undefined)
|
|
634
|
+
coords.push({ x, y });
|
|
635
|
+
}
|
|
636
|
+
if (coords.length < 2)
|
|
637
|
+
return undefined;
|
|
638
|
+
let waypoints;
|
|
639
|
+
const segments = [];
|
|
640
|
+
if (isCubic && coords.length >= 4) {
|
|
641
|
+
// type: "cubic" — coords are [anchor, cp1, cp2, anchor, cp1, cp2, anchor, ...]
|
|
642
|
+
// Every 3rd coord starting from 0 is an anchor, the two between are control points.
|
|
643
|
+
waypoints = [];
|
|
644
|
+
waypoints.push(coords[0]);
|
|
645
|
+
for (let i = 1; i + 2 < coords.length; i += 3) {
|
|
646
|
+
const cp1 = coords[i];
|
|
647
|
+
const cp2 = coords[i + 1];
|
|
648
|
+
const anchor = coords[i + 2];
|
|
649
|
+
waypoints.push(anchor);
|
|
650
|
+
segments.push({ curviness, cp1, cp2 });
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
// Waypoint array with global curviness
|
|
655
|
+
waypoints = coords;
|
|
656
|
+
for (let i = 0; i < waypoints.length - 1; i++) {
|
|
657
|
+
segments.push({ curviness });
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
661
|
+
arcPath: { enabled: true, autoRotate, segments },
|
|
662
|
+
waypoints,
|
|
663
|
+
};
|
|
664
|
+
}
|
|
590
665
|
// fallow-ignore-next-line complexity
|
|
591
666
|
function tweenCallToAnimation(call, scope) {
|
|
592
667
|
const vars = objectExpressionToRecord(call.varsArg, scope);
|
|
@@ -594,6 +669,7 @@ function tweenCallToAnimation(call, scope) {
|
|
|
594
669
|
const extras = {};
|
|
595
670
|
let keyframesData;
|
|
596
671
|
let hasUnresolvedKeyframes = false;
|
|
672
|
+
let motionPathResult;
|
|
597
673
|
for (const [key, val] of Object.entries(vars)) {
|
|
598
674
|
if (BUILTIN_VAR_KEYS.has(key))
|
|
599
675
|
continue;
|
|
@@ -606,6 +682,11 @@ function tweenCallToAnimation(call, scope) {
|
|
|
606
682
|
hasUnresolvedKeyframes = true;
|
|
607
683
|
continue;
|
|
608
684
|
}
|
|
685
|
+
if (key === "motionPath") {
|
|
686
|
+
const mpNode = findPropertyNode(call.varsArg, "motionPath");
|
|
687
|
+
motionPathResult = parseMotionPathNode(mpNode, scope);
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
609
690
|
if (key === "easeEach") {
|
|
610
691
|
// easeEach is only meaningful alongside keyframes — handled below.
|
|
611
692
|
continue;
|
|
@@ -630,6 +711,30 @@ function tweenCallToAnimation(call, scope) {
|
|
|
630
711
|
if (keyframesData && typeof vars.easeEach === "string") {
|
|
631
712
|
keyframesData.easeEach = vars.easeEach;
|
|
632
713
|
}
|
|
714
|
+
// When motionPath is present, reconstruct x/y as keyframe waypoints.
|
|
715
|
+
if (motionPathResult) {
|
|
716
|
+
const { waypoints } = motionPathResult;
|
|
717
|
+
if (!keyframesData) {
|
|
718
|
+
// No explicit keyframes — create synthetic percentage keyframes from waypoints.
|
|
719
|
+
const kf = waypoints.map((wp, i) => ({
|
|
720
|
+
percentage: waypoints.length > 1 ? Math.round((i / (waypoints.length - 1)) * 100) : 0,
|
|
721
|
+
properties: { x: wp.x, y: wp.y },
|
|
722
|
+
}));
|
|
723
|
+
keyframesData = { format: "percentage", keyframes: kf };
|
|
724
|
+
}
|
|
725
|
+
else {
|
|
726
|
+
// Merge waypoint positions into existing keyframes at matching percentages.
|
|
727
|
+
// If keyframe count matches waypoint count, assign positionally.
|
|
728
|
+
const kfs = keyframesData.keyframes;
|
|
729
|
+
if (kfs.length === waypoints.length) {
|
|
730
|
+
for (let i = 0; i < kfs.length; i++) {
|
|
731
|
+
kfs[i].properties.x = waypoints[i].x;
|
|
732
|
+
kfs[i].properties.y = waypoints[i].y;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
// arcPath is attached below on the animation result.
|
|
737
|
+
}
|
|
633
738
|
let fromProperties;
|
|
634
739
|
if (call.method === "fromTo" && call.fromArg) {
|
|
635
740
|
fromProperties = {};
|
|
@@ -657,6 +762,8 @@ function tweenCallToAnimation(call, scope) {
|
|
|
657
762
|
anim.extras = extras;
|
|
658
763
|
if (keyframesData)
|
|
659
764
|
anim.keyframes = keyframesData;
|
|
765
|
+
if (motionPathResult)
|
|
766
|
+
anim.arcPath = motionPathResult.arcPath;
|
|
660
767
|
if (hasUnresolvedKeyframes)
|
|
661
768
|
anim.hasUnresolvedKeyframes = true;
|
|
662
769
|
if (call.selector === "__unresolved__")
|
|
@@ -809,8 +916,16 @@ function applyUpdatesToCall(call, updates) {
|
|
|
809
916
|
}
|
|
810
917
|
if (updates.duration !== undefined)
|
|
811
918
|
setVarsKey(call.varsArg, "duration", updates.duration);
|
|
812
|
-
if (updates.ease !== undefined)
|
|
813
|
-
|
|
919
|
+
if (updates.ease !== undefined) {
|
|
920
|
+
const kfNode = findKeyframesObjectNode(call.varsArg);
|
|
921
|
+
if (kfNode) {
|
|
922
|
+
setVarsKey(kfNode, "easeEach", updates.ease);
|
|
923
|
+
removeVarsKey(call.varsArg, "ease");
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
setVarsKey(call.varsArg, "ease", updates.ease);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
814
929
|
if (updates.position !== undefined) {
|
|
815
930
|
const posIdx = call.method === "fromTo" ? 3 : 2;
|
|
816
931
|
call.node.arguments[posIdx] = parseExpr(valueToCode(updates.position));
|
|
@@ -893,6 +1008,49 @@ export function addAnimationToScript(script, animation) {
|
|
|
893
1008
|
}
|
|
894
1009
|
return { script: recast.print(parsed.ast).code, id };
|
|
895
1010
|
}
|
|
1011
|
+
export function addAnimationWithKeyframesToScript(script, targetSelector, position, duration, keyframes, ease) {
|
|
1012
|
+
let parsed;
|
|
1013
|
+
try {
|
|
1014
|
+
parsed = parseGsapAst(script);
|
|
1015
|
+
}
|
|
1016
|
+
catch (e) {
|
|
1017
|
+
console.warn("[gsap-parser] addAnimationWithKeyframesToScript parse failed:", e);
|
|
1018
|
+
return { script, id: "" };
|
|
1019
|
+
}
|
|
1020
|
+
if (parsed.located.length === 0 && parsed.detection.timelineVar === null) {
|
|
1021
|
+
return { script, id: "" };
|
|
1022
|
+
}
|
|
1023
|
+
const selector = JSON.stringify(targetSelector);
|
|
1024
|
+
const kfEntries = keyframes.map((kf) => {
|
|
1025
|
+
const propEntries = Object.entries(kf.properties).map(([k, v]) => `${safeKey(k)}: ${valueToCode(v)}`);
|
|
1026
|
+
if (kf.ease)
|
|
1027
|
+
propEntries.push(`ease: ${JSON.stringify(kf.ease)}`);
|
|
1028
|
+
if (kf.auto)
|
|
1029
|
+
propEntries.push(`_auto: 1`);
|
|
1030
|
+
return `${JSON.stringify(`${kf.percentage}%`)}: { ${propEntries.join(", ")} }`;
|
|
1031
|
+
});
|
|
1032
|
+
const kfCode = `{ ${kfEntries.join(", ")} }`;
|
|
1033
|
+
const varEntries = [`keyframes: ${kfCode}`, `duration: ${valueToCode(duration)}`];
|
|
1034
|
+
if (ease)
|
|
1035
|
+
varEntries.push(`ease: ${JSON.stringify(ease)}`);
|
|
1036
|
+
const posCode = valueToCode(position);
|
|
1037
|
+
const stmtCode = `${parsed.timelineVar}.to(${selector}, { ${varEntries.join(", ")} }, ${posCode});`;
|
|
1038
|
+
const newStatement = parseScript(stmtCode).program.body[0];
|
|
1039
|
+
const lastCall = parsed.located[parsed.located.length - 1]?.call;
|
|
1040
|
+
const anchorPath = lastCall
|
|
1041
|
+
? findStatementPath(lastCall.path)
|
|
1042
|
+
: findTimelineDeclarationPath(parsed.ast, parsed.timelineVar);
|
|
1043
|
+
if (anchorPath) {
|
|
1044
|
+
anchorPath.insertAfter(newStatement);
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
parsed.ast.program.body.push(newStatement);
|
|
1048
|
+
}
|
|
1049
|
+
const result = recast.print(parsed.ast).code;
|
|
1050
|
+
const reParsed = parseGsapAst(result);
|
|
1051
|
+
const newId = reParsed.located[reParsed.located.length - 1]?.id ?? "";
|
|
1052
|
+
return { script: result, id: newId };
|
|
1053
|
+
}
|
|
896
1054
|
/** Find the statement path of `const <timelineVar> = gsap.timeline(...)`. */
|
|
897
1055
|
function findTimelineDeclarationPath(ast, timelineVar) {
|
|
898
1056
|
let found = null;
|
|
@@ -1023,12 +1181,27 @@ function collapseKeyframesToFlat(varsArg, record) {
|
|
|
1023
1181
|
* object. If the percentage already exists, its value is replaced.
|
|
1024
1182
|
*/
|
|
1025
1183
|
export function addKeyframeToScript(script, animationId, percentage, properties, ease, backfillDefaults) {
|
|
1026
|
-
|
|
1184
|
+
let loc = locateAnimation(script, animationId);
|
|
1185
|
+
if (!loc) {
|
|
1186
|
+
const convertedId = animationId.replace(/-from-|-fromTo-/, "-to-");
|
|
1187
|
+
loc = locateAnimation(script, convertedId);
|
|
1188
|
+
}
|
|
1027
1189
|
if (!loc)
|
|
1028
1190
|
return script;
|
|
1029
|
-
|
|
1030
|
-
if (!kfNode)
|
|
1031
|
-
|
|
1191
|
+
let kfNode = findKeyframesObjectNode(loc.target.call.varsArg);
|
|
1192
|
+
if (!kfNode) {
|
|
1193
|
+
script = convertToKeyframesInScript(script, animationId);
|
|
1194
|
+
loc = locateAnimation(script, animationId);
|
|
1195
|
+
if (!loc) {
|
|
1196
|
+
const convertedId = animationId.replace(/-from-|-fromTo-/, "-to-");
|
|
1197
|
+
loc = locateAnimation(script, convertedId);
|
|
1198
|
+
}
|
|
1199
|
+
if (!loc)
|
|
1200
|
+
return script;
|
|
1201
|
+
kfNode = findKeyframesObjectNode(loc.target.call.varsArg);
|
|
1202
|
+
if (!kfNode)
|
|
1203
|
+
return script;
|
|
1204
|
+
}
|
|
1032
1205
|
const pctKey = `${percentage}%`;
|
|
1033
1206
|
const newValueNode = buildKeyframeValueNode(properties, ease);
|
|
1034
1207
|
// Replace if this percentage already exists
|
|
@@ -1053,6 +1226,21 @@ export function addKeyframeToScript(script, animationId, percentage, properties,
|
|
|
1053
1226
|
}
|
|
1054
1227
|
kfNode.properties.splice(insertIdx, 0, newProp);
|
|
1055
1228
|
}
|
|
1229
|
+
// Auto-update 100%: if the 100% keyframe still has `_auto: 1` (never
|
|
1230
|
+
// explicitly edited by the user), update it to match the new keyframe's
|
|
1231
|
+
// values so the element holds its final position instead of snapping back.
|
|
1232
|
+
// Once the user drags at 100%, `_auto` is gone and we stop touching it.
|
|
1233
|
+
if (percentage < 100 && percentage !== 0) {
|
|
1234
|
+
const pctProps = filterPercentageProps(kfNode);
|
|
1235
|
+
const hundredProp = pctProps.find((p) => percentageFromKey(propKeyName(p) ?? "") === 100);
|
|
1236
|
+
if (hundredProp?.value?.type === "ObjectExpression") {
|
|
1237
|
+
const hasAuto = hundredProp.value.properties.some((p) => isObjectProperty(p) && propKeyName(p) === "_auto");
|
|
1238
|
+
if (hasAuto) {
|
|
1239
|
+
const updatedProps = { ...properties, _auto: 1 };
|
|
1240
|
+
hundredProp.value = buildKeyframeValueNode(updatedProps, undefined);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1056
1244
|
// Backfill: when the new keyframe introduces properties absent from other
|
|
1057
1245
|
// keyframes, add default values so GSAP can interpolate them.
|
|
1058
1246
|
if (backfillDefaults) {
|
|
@@ -1271,6 +1459,184 @@ export function materializeKeyframesInScript(script, animationId, keyframes, eas
|
|
|
1271
1459
|
removeVarsKey(varsArg, "easeEach");
|
|
1272
1460
|
return recast.print(loc.parsed.ast).code;
|
|
1273
1461
|
}
|
|
1462
|
+
// ── Arc Path (motionPath) AST Mutations ──────────────────────────────────
|
|
1463
|
+
function buildMotionPathObjectCode(config) {
|
|
1464
|
+
const { waypoints, segments, autoRotate } = config;
|
|
1465
|
+
const hasExplicitControlPoints = segments.some((s) => s.cp1 && s.cp2);
|
|
1466
|
+
let pathEntries;
|
|
1467
|
+
if (hasExplicitControlPoints && waypoints.length >= 2) {
|
|
1468
|
+
// type: "cubic" — interleave control points: [anchor, cp1, cp2, anchor, ...]
|
|
1469
|
+
pathEntries = [`{x: ${waypoints[0].x}, y: ${waypoints[0].y}}`];
|
|
1470
|
+
for (let i = 0; i < segments.length; i++) {
|
|
1471
|
+
const seg = segments[i];
|
|
1472
|
+
const nextWp = waypoints[i + 1];
|
|
1473
|
+
if (seg.cp1 && seg.cp2) {
|
|
1474
|
+
pathEntries.push(`{x: ${seg.cp1.x}, y: ${seg.cp1.y}}`);
|
|
1475
|
+
pathEntries.push(`{x: ${seg.cp2.x}, y: ${seg.cp2.y}}`);
|
|
1476
|
+
}
|
|
1477
|
+
else {
|
|
1478
|
+
// Auto-generate simple midpoint control points from curviness
|
|
1479
|
+
const wp = waypoints[i];
|
|
1480
|
+
const dx = nextWp.x - wp.x;
|
|
1481
|
+
const dy = nextWp.y - wp.y;
|
|
1482
|
+
const c = seg.curviness ?? 1;
|
|
1483
|
+
pathEntries.push(`{x: ${wp.x + dx * 0.33}, y: ${wp.y + dy * 0.33 - c * Math.abs(dx) * 0.25}}`);
|
|
1484
|
+
pathEntries.push(`{x: ${wp.x + dx * 0.66}, y: ${wp.y + dy * 0.66 - c * Math.abs(dx) * 0.25}}`);
|
|
1485
|
+
}
|
|
1486
|
+
pathEntries.push(`{x: ${nextWp.x}, y: ${nextWp.y}}`);
|
|
1487
|
+
}
|
|
1488
|
+
const pathStr = pathEntries.join(", ");
|
|
1489
|
+
const parts = [`path: [${pathStr}]`, `type: "cubic"`];
|
|
1490
|
+
if (autoRotate === true)
|
|
1491
|
+
parts.push("autoRotate: true");
|
|
1492
|
+
else if (typeof autoRotate === "number")
|
|
1493
|
+
parts.push(`autoRotate: ${autoRotate}`);
|
|
1494
|
+
return `{ ${parts.join(", ")} }`;
|
|
1495
|
+
}
|
|
1496
|
+
// Simple waypoint array with curviness
|
|
1497
|
+
pathEntries = waypoints.map((wp) => `{x: ${wp.x}, y: ${wp.y}}`);
|
|
1498
|
+
const curviness = segments[0]?.curviness ?? 1;
|
|
1499
|
+
const parts = [`path: [${pathEntries.join(", ")}]`];
|
|
1500
|
+
if (curviness !== 1)
|
|
1501
|
+
parts.push(`curviness: ${curviness}`);
|
|
1502
|
+
if (autoRotate === true)
|
|
1503
|
+
parts.push("autoRotate: true");
|
|
1504
|
+
else if (typeof autoRotate === "number")
|
|
1505
|
+
parts.push(`autoRotate: ${autoRotate}`);
|
|
1506
|
+
return `{ ${parts.join(", ")} }`;
|
|
1507
|
+
}
|
|
1508
|
+
export function setArcPathInScript(script, animationId, config) {
|
|
1509
|
+
const loc = locateAnimation(script, animationId);
|
|
1510
|
+
if (!loc)
|
|
1511
|
+
return script;
|
|
1512
|
+
const varsArg = loc.target.call.varsArg;
|
|
1513
|
+
const anim = loc.target.animation;
|
|
1514
|
+
if (!config.enabled) {
|
|
1515
|
+
// Disable arc: restore x/y from motionPath's last waypoint, then remove motionPath
|
|
1516
|
+
const motionPathProp = varsArg.properties.find((p) => isObjectProperty(p) && propKeyName(p) === "motionPath");
|
|
1517
|
+
if (motionPathProp) {
|
|
1518
|
+
const mpVal = motionPathProp.value;
|
|
1519
|
+
let pathArr;
|
|
1520
|
+
if (mpVal?.type === "ObjectExpression") {
|
|
1521
|
+
const pathProp = mpVal.properties.find((p) => isObjectProperty(p) && propKeyName(p) === "path");
|
|
1522
|
+
if (pathProp?.value?.type === "ArrayExpression")
|
|
1523
|
+
pathArr = pathProp.value.elements;
|
|
1524
|
+
}
|
|
1525
|
+
if (pathArr && pathArr.length > 0) {
|
|
1526
|
+
const last = pathArr[pathArr.length - 1];
|
|
1527
|
+
if (last?.type === "ObjectExpression") {
|
|
1528
|
+
for (const p of last.properties) {
|
|
1529
|
+
const k = propKeyName(p);
|
|
1530
|
+
if (k === "x" || k === "y") {
|
|
1531
|
+
const v = p.value?.value;
|
|
1532
|
+
if (typeof v === "number")
|
|
1533
|
+
setVarsKey(varsArg, k, v);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
removeVarsKey(varsArg, "motionPath");
|
|
1540
|
+
return recast.print(loc.parsed.ast).code;
|
|
1541
|
+
}
|
|
1542
|
+
// Extract x/y waypoints from keyframes or flat tween properties
|
|
1543
|
+
const kfs = anim.keyframes?.keyframes ?? [];
|
|
1544
|
+
const waypoints = [];
|
|
1545
|
+
for (const kf of kfs) {
|
|
1546
|
+
const x = typeof kf.properties.x === "number" ? kf.properties.x : undefined;
|
|
1547
|
+
const y = typeof kf.properties.y === "number" ? kf.properties.y : undefined;
|
|
1548
|
+
if (x !== undefined && y !== undefined)
|
|
1549
|
+
waypoints.push({ x, y });
|
|
1550
|
+
}
|
|
1551
|
+
// For flat tweens with x/y in properties, synthesize start → end waypoints
|
|
1552
|
+
if (waypoints.length < 2) {
|
|
1553
|
+
const px = anim.properties.x;
|
|
1554
|
+
const py = anim.properties.y;
|
|
1555
|
+
if (typeof px === "number" || typeof py === "number") {
|
|
1556
|
+
waypoints.length = 0;
|
|
1557
|
+
waypoints.push({ x: 0, y: 0 });
|
|
1558
|
+
waypoints.push({ x: typeof px === "number" ? px : 0, y: typeof py === "number" ? py : 0 });
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
if (waypoints.length < 2)
|
|
1562
|
+
return script;
|
|
1563
|
+
// Build segments — use provided segments or create defaults
|
|
1564
|
+
const segments = config.segments.length === waypoints.length - 1
|
|
1565
|
+
? config.segments
|
|
1566
|
+
: Array.from({ length: waypoints.length - 1 }, () => ({ curviness: 1 }));
|
|
1567
|
+
const motionPathCode = buildMotionPathObjectCode({
|
|
1568
|
+
waypoints,
|
|
1569
|
+
segments,
|
|
1570
|
+
autoRotate: config.autoRotate,
|
|
1571
|
+
});
|
|
1572
|
+
// Set motionPath on the vars
|
|
1573
|
+
const motionPathNode = parseExpr(motionPathCode);
|
|
1574
|
+
const existingProp = varsArg.properties.find((p) => isObjectProperty(p) && propKeyName(p) === "motionPath");
|
|
1575
|
+
if (existingProp) {
|
|
1576
|
+
existingProp.value = motionPathNode;
|
|
1577
|
+
}
|
|
1578
|
+
else {
|
|
1579
|
+
const prop = parseExpr(`{ motionPath: ${motionPathCode} }`).properties[0];
|
|
1580
|
+
varsArg.properties.push(prop);
|
|
1581
|
+
}
|
|
1582
|
+
// Strip x/y from keyframes (they're now in motionPath)
|
|
1583
|
+
const kfNode = findKeyframesObjectNode(varsArg);
|
|
1584
|
+
if (kfNode) {
|
|
1585
|
+
for (const pctProp of filterPercentageProps(kfNode)) {
|
|
1586
|
+
if (pctProp.value?.type === "ObjectExpression") {
|
|
1587
|
+
pctProp.value.properties = pctProp.value.properties.filter((p) => {
|
|
1588
|
+
const k = propKeyName(p);
|
|
1589
|
+
return k !== "x" && k !== "y";
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
// Strip flat x/y from vars (they're now in motionPath)
|
|
1595
|
+
removeVarsKey(varsArg, "x");
|
|
1596
|
+
removeVarsKey(varsArg, "y");
|
|
1597
|
+
return recast.print(loc.parsed.ast).code;
|
|
1598
|
+
}
|
|
1599
|
+
export function updateArcSegmentInScript(script, animationId, segmentIndex, update) {
|
|
1600
|
+
const loc = locateAnimation(script, animationId);
|
|
1601
|
+
if (!loc)
|
|
1602
|
+
return script;
|
|
1603
|
+
const anim = loc.target.animation;
|
|
1604
|
+
if (!anim.arcPath?.enabled)
|
|
1605
|
+
return script;
|
|
1606
|
+
const segments = [...anim.arcPath.segments];
|
|
1607
|
+
if (segmentIndex < 0 || segmentIndex >= segments.length)
|
|
1608
|
+
return script;
|
|
1609
|
+
segments[segmentIndex] = { ...segments[segmentIndex], ...update };
|
|
1610
|
+
// Rebuild the full motionPath with updated segments
|
|
1611
|
+
const kfs = anim.keyframes?.keyframes ?? [];
|
|
1612
|
+
const waypoints = [];
|
|
1613
|
+
for (const kf of kfs) {
|
|
1614
|
+
const x = typeof kf.properties.x === "number" ? kf.properties.x : undefined;
|
|
1615
|
+
const y = typeof kf.properties.y === "number" ? kf.properties.y : undefined;
|
|
1616
|
+
if (x !== undefined && y !== undefined)
|
|
1617
|
+
waypoints.push({ x, y });
|
|
1618
|
+
}
|
|
1619
|
+
if (waypoints.length < 2)
|
|
1620
|
+
return script;
|
|
1621
|
+
const motionPathCode = buildMotionPathObjectCode({
|
|
1622
|
+
waypoints,
|
|
1623
|
+
segments,
|
|
1624
|
+
autoRotate: anim.arcPath.autoRotate,
|
|
1625
|
+
});
|
|
1626
|
+
const varsArg = loc.target.call.varsArg;
|
|
1627
|
+
const existingProp = varsArg.properties.find((p) => isObjectProperty(p) && propKeyName(p) === "motionPath");
|
|
1628
|
+
if (existingProp) {
|
|
1629
|
+
existingProp.value = parseExpr(motionPathCode);
|
|
1630
|
+
}
|
|
1631
|
+
return recast.print(loc.parsed.ast).code;
|
|
1632
|
+
}
|
|
1633
|
+
export function removeArcPathFromScript(script, animationId) {
|
|
1634
|
+
return setArcPathInScript(script, animationId, {
|
|
1635
|
+
enabled: false,
|
|
1636
|
+
autoRotate: false,
|
|
1637
|
+
segments: [],
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1274
1640
|
/**
|
|
1275
1641
|
* Replace a dynamic loop that generates multiple tween calls with individual
|
|
1276
1642
|
* static `tl.to()` calls — one per element. Finds the loop containing the
|