mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 17:27:09 +00:00
fix(core): a Lottie animation in a mounted composition plays from that composition's start (#4471)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { createLottieAdapter } from "./lottie";
|
||||
import { createRuntimeStartTimeResolver } from "../startResolver";
|
||||
|
||||
const lottieWindow = window as Window & {
|
||||
lottie?: {
|
||||
@@ -84,6 +85,37 @@ describe("lottie adapter", () => {
|
||||
});
|
||||
|
||||
describe("seek", () => {
|
||||
function mountedAt(start: string) {
|
||||
document.body.innerHTML = `<div data-composition-id="root"><div data-composition-id="host" data-start="${start}"><div data-composition-id="walk"><div id="player"></div></div></div></div>`;
|
||||
const { resolveStartForElement } = createRuntimeStartTimeResolver({});
|
||||
const adapter = createLottieAdapter({
|
||||
resolveStartSeconds: (el) => resolveStartForElement(el, 0),
|
||||
});
|
||||
return { adapter, player: document.getElementById("player")! };
|
||||
}
|
||||
|
||||
it("seeks a player in a mounted composition to the time since that composition started", () => {
|
||||
const { adapter, player } = mountedAt("3");
|
||||
const anim = { ...createLottieWebAnim(), wrapper: player };
|
||||
const dot = { ...createDotLottiePlayer({ totalFrames: 60, frameRate: 30 }), canvas: player };
|
||||
lottieWindow.__hfLottie = [anim, dot];
|
||||
adapter.seek({ time: 4 });
|
||||
expect(anim.goToAndStop).toHaveBeenCalledWith(1000, false);
|
||||
expect(dot.setCurrentRawFrameValue).toHaveBeenCalledWith(30);
|
||||
adapter.seek({ time: 1 });
|
||||
expect(anim.goToAndStop).toHaveBeenLastCalledWith(0, false);
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
it("keeps page time for a player in a composition that starts at 0", () => {
|
||||
const { adapter, player } = mountedAt("0");
|
||||
const anim = { ...createLottieWebAnim(), wrapper: player };
|
||||
lottieWindow.__hfLottie = [anim];
|
||||
adapter.seek({ time: 2 });
|
||||
expect(anim.goToAndStop).toHaveBeenCalledWith(2000, false);
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
it("seeks lottie-web with goToAndStop in ms", () => {
|
||||
const anim = createLottieWebAnim();
|
||||
lottieWindow.__hfLottie = [anim];
|
||||
|
||||
@@ -49,7 +49,19 @@ import { swallow } from "../diagnostics";
|
||||
* via the global `lottie` object, so compositions that call
|
||||
* `lottie.loadAnimation(...)` without manually registering still work.
|
||||
*/
|
||||
export function createLottieAdapter(): RuntimeDeterministicAdapter {
|
||||
export function createLottieAdapter(params?: {
|
||||
resolveStartSeconds?: (element: Element) => number;
|
||||
}): RuntimeDeterministicAdapter {
|
||||
const secondsIntoComposition = (anim: unknown, pageTime: number): number => {
|
||||
const el = isLottieWebAnimation(anim) ? anim.wrapper : (anim as DotLottiePlayer).canvas;
|
||||
const composition =
|
||||
el && typeof (el as Element).closest === "function"
|
||||
? (el as Element).closest("[data-composition-id]")
|
||||
: null;
|
||||
if (!composition || !params?.resolveStartSeconds) return pageTime;
|
||||
return Math.max(0, pageTime - params.resolveStartSeconds(composition));
|
||||
};
|
||||
|
||||
return {
|
||||
name: "lottie",
|
||||
|
||||
@@ -78,12 +90,13 @@ export function createLottieAdapter(): RuntimeDeterministicAdapter {
|
||||
},
|
||||
|
||||
seek: (ctx) => {
|
||||
const time = Math.max(0, Number(ctx.time) || 0);
|
||||
const pageTime = Math.max(0, Number(ctx.time) || 0);
|
||||
const instances = (window as LottieWindow).__hfLottie;
|
||||
if (!instances || instances.length === 0) return;
|
||||
|
||||
for (const anim of instances) {
|
||||
try {
|
||||
const time = secondsIntoComposition(anim, pageTime);
|
||||
const loops = anim.loop === true;
|
||||
if (isLottieWebAnimation(anim)) {
|
||||
// lottie-web: AnimationItem
|
||||
@@ -245,6 +258,7 @@ interface LottieWebAnimation {
|
||||
totalFrames: number;
|
||||
frameRate: number;
|
||||
loop?: boolean | number;
|
||||
wrapper?: unknown;
|
||||
}
|
||||
|
||||
interface LottieWebGlobal {
|
||||
@@ -261,6 +275,7 @@ interface DotLottiePlayer {
|
||||
frameRate?: number;
|
||||
duration?: number;
|
||||
loop?: boolean;
|
||||
canvas?: unknown;
|
||||
}
|
||||
|
||||
interface LottieWindow extends Window {
|
||||
|
||||
@@ -3269,7 +3269,9 @@ export function initSandboxRuntimeModular(): void {
|
||||
resolveStartSeconds: (element) => resolveStartForElement(element, 0),
|
||||
}),
|
||||
createAnimeJsAdapter(),
|
||||
createLottieAdapter(),
|
||||
createLottieAdapter({
|
||||
resolveStartSeconds: (element) => resolveStartForElement(element, 0),
|
||||
}),
|
||||
createThreeAdapter(),
|
||||
createMapboxAdapter(),
|
||||
createLeafletAdapter(),
|
||||
|
||||
Reference in New Issue
Block a user