(
elementId: string,
keyframes: Keyframe[],
elementStartTime: number,
base?: { x?: number; y?: number; scale?: number },
)
| 336 | // ── Keyframe Conversion Helpers ───────────────────────────────────────────── |
| 337 | |
| 338 | export function keyframesToGsapAnimations( |
| 339 | elementId: string, |
| 340 | keyframes: Keyframe[], |
| 341 | elementStartTime: number, |
| 342 | base?: { x?: number; y?: number; scale?: number }, |
| 343 | ): GsapAnimation[] { |
| 344 | const sorted = [...keyframes].sort((a, b) => a.time - b.time); |
| 345 | const animations: GsapAnimation[] = []; |
| 346 | const baseX = base?.x ?? 0; |
| 347 | const baseY = base?.y ?? 0; |
| 348 | const baseScale = base?.scale ?? 1; |
| 349 | |
| 350 | // fallow-ignore-next-line complexity |
| 351 | sorted.forEach((kf, i) => { |
| 352 | const absoluteTime = elementStartTime + kf.time; |
| 353 | const isFirst = i === 0; |
| 354 | const prevKf = i > 0 ? sorted[i - 1] : null; |
| 355 | const duration = prevKf ? kf.time - prevKf.time : undefined; |
| 356 | const position = prevKf ? elementStartTime + prevKf.time : absoluteTime; |
| 357 | |
| 358 | const properties: Record<string, number | string> = {}; |
| 359 | for (const [key, value] of Object.entries(kf.properties)) { |
| 360 | if (typeof value !== "number") continue; |
| 361 | if (key === "x") properties.x = baseX + value; |
| 362 | else if (key === "y") properties.y = baseY + value; |
| 363 | else if (key === "scale") properties.scale = baseScale * value; |
| 364 | else properties[key] = value; |
| 365 | } |
| 366 | |
| 367 | animations.push({ |
| 368 | id: `${elementId}-kf-${kf.id}`, |
| 369 | targetSelector: `#${elementId}`, |
| 370 | method: isFirst ? "set" : "to", |
| 371 | position, |
| 372 | properties, |
| 373 | duration: isFirst ? undefined : duration, |
| 374 | ease: kf.ease, |
| 375 | }); |
| 376 | }); |
| 377 | |
| 378 | return animations; |
| 379 | } |
| 380 | |
| 381 | export function gsapAnimationsToKeyframes( |
| 382 | animations: GsapAnimation[], |
no test coverage detected