diff --git a/src/components/ScrollerLottie/ScrollerLottie.mdx b/src/components/ScrollerLottie/ScrollerLottie.mdx index 8a7e8ef2..3bcd8678 100644 --- a/src/components/ScrollerLottie/ScrollerLottie.mdx +++ b/src/components/ScrollerLottie/ScrollerLottie.mdx @@ -9,6 +9,10 @@ import CompositionMarkerImage from './assets/marker.png?url'; The `ScrollerLottie` component plays Lottie animations. It uses the [dotLottie-web](https://developers.lottiefiles.com/docs/dotlottie-player/dotlottie-web/) library to render the animations. +## How to use .lottie files + +LottieFiles is the official platform for creating and editing Lottie animations, and exporting them in the dotLottie format for smaller file sizes. The free version of LottieFiles has limited features, so [Bodymovin](https://exchange.adobe.com/apps/cc/12557/bodymovin) remains a popular, free way to export animations as JSON files. You can use the [LottieFiles converter](https://lottiefiles.com/tools/lottie-to-dotlottie) to convert JSON files to dotLottie or optimized dotLottie formats. This component is flexible and supports both dotLottie and JSON animation files. + ## Basic demo To use the `ScrollerLottie` component, import it and provide the animation source. The height defaults to `100lvh`, but you can adjust this to any valid CSS height value such as `1200px` or `200lvh` with the `height` prop. @@ -27,7 +31,7 @@ With the graphics kit, you'll likely get your text and prop values from an Archi # ArchieML doc [blocks] type: lottie - src: LottieFile + src: LottieFile.lottie :end [] ``` @@ -37,13 +41,14 @@ With the graphics kit, you'll likely get your text and prop values from an Archi ```svelte {#each content.blocks as block} {#if block.type == 'lottie'} import { ScrollerLottie } from '@reuters-graphics/graphics-components'; + import { assets } from '$app/paths'; {#each content.blocks as block} {#if block.type == 'lottie'} import { ScrollerLottie } from '@reuters-graphics/graphics-components'; + import { assets } from '$app/paths'; {#each content.blocks as block} {#if block.type == 'lottie'} import { ScrollerLottie } from '@reuters-graphics/graphics-components'; + import { assets } from '$app/paths'; {#each content.blocks as block} {#if block.type == 'lottie'} - import { ScrollerLottie } from '@reuters-graphics/graphics-components'; + import { + ScrollerLottie, + ScrollerLottieForeground, + } from '@reuters-graphics/graphics-components'; + import { assets } from '$app/paths'; const Components = $state({ Headline, @@ -315,7 +327,7 @@ With the graphics kit, you'll likely get your text and prop values from an Archi {#if block.type == 'lottie'} x.name); @@ -116,13 +115,13 @@ start = segment ? segment[0] : 0; end = segment ? segment[1] : lottiePlayer.totalFrames - 1; } + + // set to frame 1 to trigger initial render + // helpful especially when themeId is set + lottiePlayer.setFrame(1); + + onLoad(); // call user-defined onLoad function } - - // set to frame 1 to trigger initial render - // helpful especially when themeId is set - lottiePlayer.setFrame(1); - - onLoad(); // call user-defined onLoad function } function onCompleteEvent() { @@ -152,23 +151,73 @@ if (lottiePlayer && lottieState) { keys.forEach((key) => { - lottieState[key] = lottiePlayer[key]; + switch (key) { + case 'currentFrame': + lottieState.currentFrame = lottiePlayer!.currentFrame; + break; + case 'totalFrames': + lottieState.totalFrames = lottiePlayer!.totalFrames; + break; + case 'duration': + lottieState.duration = lottiePlayer!.duration; + break; + case 'loop': + lottieState.loop = lottiePlayer!.loop; + break; + case 'speed': + lottieState.speed = lottiePlayer!.speed; + break; + case 'loopCount': + lottieState.loopCount = lottiePlayer!.loopCount; + break; + case 'mode': + lottieState.mode = lottiePlayer!.mode; + break; + case 'isPaused': + lottieState.isPaused = lottiePlayer!.isPaused; + break; + case 'isPlaying': + lottieState.isPlaying = lottiePlayer!.isPlaying; + break; + case 'isStopped': + lottieState.isStopped = lottiePlayer!.isStopped; + break; + case 'isLoaded': + lottieState.isLoaded = lottiePlayer!.isLoaded; + break; + case 'isFrozen': + lottieState.isFrozen = lottiePlayer!.isFrozen; + break; + case 'segment': + lottieState.segment = lottiePlayer!.segment ?? null; + break; + case 'autoplay': + lottieState.autoplay = lottiePlayer!.autoplay ?? false; + break; + case 'layout': + lottieState.layout = lottiePlayer!.layout ?? null; + break; + case 'activeThemeId': + lottieState.activeThemeId = lottiePlayer!.activeThemeId ?? null; + break; + case 'marker': + lottieState.marker = lottiePlayer!.marker ?? undefined; + break; + } }); + + progress = (lottiePlayer.currentFrame + 1) / lottiePlayer.totalFrames; + lottieState.progress = progress; + onRender(); // call user-defined onRender function } - - progress = (lottiePlayer.currentFrame + 1) / lottiePlayer.totalFrames; - - lottieState.progress = progress; - - onRender(); // call user-defined onRender function } - const hoverHandler = (event) => { - if (!playOnHover || !lottiePlayer.isLoaded) return; + const hoverHandler = (event: MouseEvent) => { + if (!playOnHover || !lottiePlayer?.isLoaded) return; if (event.type === 'mouseenter') { - lottiePlayer.play(); + lottiePlayer?.play(); } else if (event.type === 'mouseleave') { - lottiePlayer.pause(); + lottiePlayer?.pause(); } }; @@ -216,7 +265,7 @@ return () => { canvas.removeEventListener('mouseenter', hoverHandler); canvas.removeEventListener('mouseleave', hoverHandler); - lottiePlayer.destroy(); + lottiePlayer?.destroy(); }; }); @@ -232,12 +281,12 @@ $effect(() => { if (lottieState.isLoaded && lottieState.progress !== progress) { autoplay = false; - lottiePlayer.pause(); + lottiePlayer?.pause(); loop = false; if (progress >= 0 && progress <= 1) { if (lottieState.isFrozen) { - lottiePlayer.unfreeze(); + lottiePlayer?.unfreeze(); lottieState.isFrozen = false; } const targetFrame = map( @@ -258,7 +307,7 @@ } else { progressTween.target = progress < 0 ? start : end; } - lottiePlayer.freeze(); + lottiePlayer?.freeze(); lottieState.isFrozen = true; } } @@ -267,7 +316,7 @@ // Tweens to progress value $effect(() => { if (progressTween.current >= 0) { - lottiePlayer.setFrame(progressTween.current); + lottiePlayer?.setFrame(progressTween.current); } }); @@ -275,7 +324,7 @@ $effect(() => { if ( typeof layout === 'object' && - lottiePlayer.isLoaded && + lottiePlayer?.isLoaded && !isEqual(layout, lottiePlayer.layout) ) { lottiePlayer.setLayout(layout); @@ -284,15 +333,15 @@ // Handles marker change $effect(() => { - if (lottieState.isLoaded && lottiePlayer.marker !== marker) { + if (lottieState.isLoaded && lottiePlayer?.marker !== marker) { if (typeof marker === 'string') { - lottiePlayer.setMarker(marker); + lottiePlayer?.setMarker(marker); start = - lottiePlayer.markers().find((m) => m.name === marker)?.time ?? 0; + lottiePlayer?.markers().find((m) => m.name === marker)?.time ?? 0; end = start + - (lottiePlayer.markers().find((m) => m.name === marker)?.duration ?? + (lottiePlayer?.markers().find((m) => m.name === marker)?.duration ?? 0); // change lottieState marker because @@ -301,7 +350,7 @@ lottieState.marker = marker; } } else if (marker === null || marker === undefined) { - lottiePlayer.setMarker(''); + lottiePlayer?.setMarker(''); } else { console.warn('Invalid marker type:', marker); } @@ -313,9 +362,9 @@ if ( lottieState.isLoaded && typeof speed == 'number' && - lottiePlayer.speed !== speed + lottiePlayer?.speed !== speed ) { - lottiePlayer.setSpeed(speed); + lottiePlayer?.setSpeed(speed); } }); @@ -324,15 +373,15 @@ if ( lottieState.isLoaded && typeof useFrameInterpolation == 'boolean' && - lottiePlayer.useFrameInterpolation !== useFrameInterpolation + lottiePlayer?.useFrameInterpolation !== useFrameInterpolation ) { - lottiePlayer.setUseFrameInterpolation(useFrameInterpolation); + lottiePlayer?.setUseFrameInterpolation(useFrameInterpolation); } }); // Handles segment change $effect(() => { - if (lottieState.isLoaded && !isEqual(lottiePlayer.segment, segment)) { + if (lottieState.isLoaded && !isEqual(lottiePlayer?.segment, segment)) { if ( Array.isArray(segment) && segment.length === 2 && @@ -340,9 +389,9 @@ typeof segment[1] === 'number' ) { let [start, end] = segment; - lottiePlayer.setSegment(start, end); + lottiePlayer?.setSegment(start, end); } else if (segment === null || segment === undefined) { - lottiePlayer.setSegment(0, lottiePlayer.totalFrames); + lottiePlayer?.setSegment(0, lottiePlayer?.totalFrames); } } }); @@ -352,9 +401,9 @@ if ( lottieState.isLoaded && typeof loop == 'boolean' && - lottiePlayer.loop !== loop + lottiePlayer?.loop !== loop ) { - lottiePlayer.setLoop(loop); + lottiePlayer?.setLoop(loop); } }); @@ -373,9 +422,9 @@ $effect(() => { if ( lottieState.isLoaded && - lottiePlayer.backgroundColor !== backgroundColor + lottiePlayer?.backgroundColor !== backgroundColor ) { - lottiePlayer.setBackgroundColor(backgroundColor || ''); + lottiePlayer?.setBackgroundColor(backgroundColor || ''); } }); @@ -384,16 +433,16 @@ if ( lottieState.isLoaded && typeof mode == 'string' && - lottiePlayer.mode !== mode + lottiePlayer?.mode !== mode ) { - lottiePlayer.setMode(mode); + lottiePlayer?.setMode(mode); } }); // Handles src change $effect(() => { if (lottieState && src !== prevSrc) { - lottiePlayer.load({ + lottiePlayer?.load({ src, autoplay, loop, @@ -441,23 +490,23 @@ $effect(() => { if ( lottieState.isLoaded && - lottiePlayer.activeAnimationId !== animationId + lottiePlayer?.activeAnimationId !== animationId ) { - lottiePlayer.loadAnimation(animationId); + lottiePlayer?.loadAnimation(animationId); } }); // Handles themeId change $effect(() => { - if (lottieState.isLoaded && lottiePlayer.activeThemeId !== themeId) { - lottiePlayer.setTheme(themeId); + if (lottieState.isLoaded && lottiePlayer?.activeThemeId !== themeId) { + lottiePlayer?.setTheme(themeId); } }); // Handles themeData change $effect(() => { - if (lottieState.isLoaded && lottiePlayer.isLoaded) { - lottiePlayer.setThemeData(themeData); + if (lottieState.isLoaded && lottiePlayer?.isLoaded) { + lottiePlayer?.setThemeData(themeData); } }); @@ -495,27 +544,4 @@ display: block; } } - - .debug-info { - position: absolute; - top: 0; - left: 0; - background-color: rgba(0, 0, 0, 0.6); - z-index: 3; - margin: 0; - min-width: 25vmin; - - .title { - width: 100%; - padding: 4px 0 0 8px; - margin: 0; - border-bottom: 1px solid rgba(255, 255, 255, 0.2); - } - - p { - color: white; - margin: 0; - padding: 4px 8px 8px 8px; - } - } diff --git a/src/components/ScrollerLottie/ScrollerLottieForeground.svelte b/src/components/ScrollerLottie/ScrollerLottieForeground.svelte index 1f777e85..6df8ac08 100644 --- a/src/components/ScrollerLottie/ScrollerLottieForeground.svelte +++ b/src/components/ScrollerLottie/ScrollerLottieForeground.svelte @@ -6,7 +6,7 @@ // Types import type { Component, Snippet } from 'svelte'; - import type { LottieState } from './lottieState.svelte'; + import type { LottieState } from './ts/lottieState.svelte'; import type { ContainerWidth, ScrollerLottieForegroundPosition, diff --git a/src/components/ScrollerLottie/ts/lottieState.svelte.ts b/src/components/ScrollerLottie/ts/lottieState.svelte.ts index e0a026ed..2b8e9712 100644 --- a/src/components/ScrollerLottie/ts/lottieState.svelte.ts +++ b/src/components/ScrollerLottie/ts/lottieState.svelte.ts @@ -1,4 +1,16 @@ +import type { Layout } from '@lottiefiles/dotlottie-web'; + export interface LottieState { + [key: string]: + | number + | boolean + | string + | null + | Array + | Array + | [number, number] + | Layout + | undefined; progress: number; currentFrame: number; totalFrames: number; @@ -14,9 +26,9 @@ export interface LottieState { isFrozen: boolean; segment: null | [number, number]; autoplay: boolean; - layout: null | string; + layout: null | Layout; allMarkers: Array; - marker: null | string; + marker: undefined | string; allThemes: Array; activeThemeId: null | string; } @@ -40,7 +52,7 @@ export function createLottieState(): LottieState { autoplay: false, layout: null, allMarkers: [], - marker: null, + marker: undefined, allThemes: [], activeThemeId: null, }); diff --git a/src/components/ScrollerVideo/demo/WithScrollerBase.svelte b/src/components/ScrollerVideo/demo/WithScrollerBase.svelte index 184bada1..6c0874c2 100644 --- a/src/components/ScrollerVideo/demo/WithScrollerBase.svelte +++ b/src/components/ScrollerVideo/demo/WithScrollerBase.svelte @@ -34,37 +34,6 @@