import { Meta } from '@storybook/blocks';

import * as ScrollerLottieStories from './ScrollerLottie.stories.svelte';
import CompositionMarkerImage from './assets/marker.png?url';

<Meta of={ScrollerLottieStories} />

# ScrollerLottie

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.

The .lottie or .json file should be placed at the same level as the component file. If using it inside `App.svelte`, create a `data` folder and place all the animation files inside. Make sure to append **?url** to the import statement when importing an animation file, as shown in the example below. This ensures that the file is treated as a URL.

> 💡TIP: Use `lvh` or `svh` units instead of `vh` unit for the height, as [these units](https://www.w3.org/TR/css-values-4/#large-viewport-size) are more reliable on mobile or other devices where elements such as the address bar toggle between being shown and hidden.

> 💡TIP: Use showDebugInfo prop to display additional information about the component state.

[Demo](?path=/story/components-graphics-scrollerlottie--basic)

With the graphics kit, you'll likely get your text and prop values from an ArchieML doc...

```yaml
# ArchieML doc
[blocks]
  type: lottie
  src: LottieFile.lottie
  :end
[]
```

... which you'll parse out of a ArchieML block object before passing to the `ScrollerLottie` component.

```svelte
<script lang="ts">
  import { ScrollerLottie } from '@reuters-graphics/graphics-components';
  import { assets } from '$app/paths';
</script>

{#each content.blocks as block}
  <!-- Inside the content.blocks for loop... -->
  {#if block.type == 'lottie'}
    <ScrollerLottie
      src={`${assets}/animations/${block.src}`}
      autoplay
      loop
      showDebugInfo
    />
  {/if}
{/each}
```

## Playing a marker

It is possible to play a specific portion of the animation using markers. Markers can be set in [AfterEffects](https://helpx.adobe.com/in/after-effects/using/layer-markers-composition-markers.html) to define separate portions of the animation. A specific marker can be played by using the `marker` prop.

The list of available markers can be found in the debug info when `showDebugInfo` prop is enabled.

> 💡NOTE: The **Comment** section of the Composition Marker dialog should only contain the name of your marker.

<img src={CompositionMarkerImage} alt="Composition Marker Dialog" />

[Demo](?path=/story/components-graphics-scrollerlottie--marker)

With the graphics kit, you'll likely get your text and prop values from an ArchieML doc...

```yaml
# ArchieML doc
[blocks]
  type: lottie
  src: LottieFile.lottie
  marker: myMarker
  :end
[]
```

... which you'll parse out of a ArchieML block object before passing to the `ScrollerLottie` component.

```svelte
<script lang="ts">
  import { ScrollerLottie } from '@reuters-graphics/graphics-components';
  import { assets } from '$app/paths';
</script>

{#each content.blocks as block}
  <!-- Inside the content.blocks for loop... -->
  {#if block.type == 'lottie'}
    <ScrollerLottie
      src={`${assets}/animations/${block.src}`}
      marker={block.marker}
      autoplay
      loop
      showDebugInfo
    />
  {/if}
{/each}
```

## Playing a segment

Just like markers, it is also possible to play a specific segment of the animation using the `segment` prop. The `segment` prop expects an array of two numbers representing the start and end frames of the segment.

[Demo](?path=/story/components-graphics-scrollerlottie--segment)

With the graphics kit, you'll likely get your text and prop values from an ArchieML doc...

```yaml
# ArchieML doc
[blocks]
  type: lottie
  src: LottieFile.lottie
  [.segment]
    start: 0
    end: 20
  []
  :end
[]
```

... which you'll parse out of a ArchieML block object before passing to the `ScrollerLottie` component.

```svelte
<script lang="ts">
  import { ScrollerLottie } from '@reuters-graphics/graphics-components';
  import { assets } from '$app/paths';
</script>

{#each content.blocks as block}
  <!-- Inside the content.blocks for loop... -->
  {#if block.type == 'lottie'}
    <ScrollerLottie
      src={`${assets}/animations/${block.src}`}
      segment={[block.segment.start, block.segment.end]}
      autoplay
      loop
      showDebugInfo
    />
  {/if}
{/each}
```

## Switching themes

[Lottie Creator](https://lottiefiles.com/theming) allows you to define multiple color themes for your animation. You can switch between these themes using the `theme` prop.

Available themes can be found in the debug info when `showDebugInfo` prop is enabled.

With the graphics kit, you'll likely get your text and prop values from an ArchieML doc...

```yaml
# ArchieML doc
[blocks]
  type: lottie
  src: LottieFile.lottie
  theme: myTheme
  :end
[]
```

... which you'll parse out of a ArchieML block object before passing to the `ScrollerLottie` component.

```svelte
<script lang="ts">
  import { ScrollerLottie } from '@reuters-graphics/graphics-components';
  import { assets } from '$app/paths';
</script>

{#each content.blocks as block}
  <!-- Inside the content.blocks for loop... -->
  {#if block.type == 'lottie'}
    <ScrollerLottie
      src={`${assets}/animations/${block.src}`}
      theme={block.theme}
      autoplay
      loop
      showDebugInfo
    />
  {/if}
{/each}
```

It is also possible to switch themes dynamically based on the `progress` prop by binding a variable to it.

[Demo](?path=/story/components-graphics-scrollerlottie--themes)

```svelte
<script lang="ts">
  import { ScrollerLottie } from '@reuters-graphics/graphics-components';
  import LottieSrc from './data/lottie-example.lottie?url';

  let progress = $state(0);
</script>

<ScrollerLottie
  src={LottieSrc}
  bind:progress
  themeId={progress < 0.33 ? 'water'
  : progress < 0.66 ? 'air'
  : 'earth'}
  autoplay
  showDebugInfo
/>
```

## With ScrollerBase

The `ScrollerLottie` component can be used in conjunction with the `ScrollerBase` component to create a more complex scrolling experience. The `ScrollerBase` component provides a scrollable container that can hold the `ScrollerLottie` component as a background.

```svelte
<script lang="ts">
  import { ScrollerLottie } from '@reuters-graphics/graphics-components';
  import LottieSrc from './data/lottie-example.lottie?url';

  // Pass `progress` as `videoPercentage` to ScrollerLottie
  let progress = $state(0);
</script>

<ScrollerBase bind:progress query="div.step-foreground-container">
  {#snippet backgroundSnippet()}
    <!-- Pass bindable prop `progress` as `progress` -->
    <div class="lottie-container">
      <ScrollerLottie src={LottieSrc} {progress} showDebugInfo />
    </div>
  {/snippet}
  {#snippet foregroundSnippet()}
    <!-- Add custom foreground HTML or component -->
    <div class="step-foreground-container">
      <h3 class="text-center">Step 1</h3>
    </div>
    <div class="step-foreground-container">
      <h3 class="text-center">Step 2</h3>
    </div>
    <div class="step-foreground-container">
      <h3 class="text-center">Step 3</h3>
    </div>
  {/snippet}
</ScrollerBase>

<style lang="scss">
  .lottie-container {
    width: 100%;
    height: 100lvh;
  }

  .step-foreground-container {
    height: 100lvh;
    width: 50%;
    padding: 1em;
    margin: auto;

    h3 {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 100%;
      color: white;
    }
  }
</style>
```

## With foregrounds

The `ScrollerLottie` component can also be used to display captions or even components, such as `Headline` or ai2svelte files, as foregrounds at specific times in the animation. To do so, add ScrollerLottieForeground components as children of the ScrollerLottie component.

[Demo](?path=/story/components-graphics-scrollerlottie--with-foregrounds)

With the graphics kit, you'll likely get your text and prop values from an ArchieML doc...

```yaml
# ArchieML doc
[blocks]
  type: lottie
  src: LottieFile.lottie

  # Array of foregrounds
  [.foregrounds]
    startFrame: 0 # When in the animation to start showing the foreground
    endFrame: 50 # When to stop showing the foreground
	type: text
	{.foregroundProps}
		text: Some text for the foreground
	{}

	startFrame: 50 # When in the animation to start showing the foreground
    endFrame: 100 # When to stop showing the foreground
	type: component
	{.foregroundProps}
		componentType: Headline
		hed: Some headline text
		dek: Some deck text
		[.authors]
			* Jane Doe
			* John Smith
		[]
	{}
  []
  :end

[]
```

... which you'll parse out of a ArchieML block object before passing to the `ScrollerLottie` component.

```svelte
<script lang="ts">
  import {
    ScrollerLottie,
    ScrollerLottieForeground,
  } from '@reuters-graphics/graphics-components';
  import { assets } from '$app/paths';

  const Components = $state({
    Headline,
    Video,
  });
</script>

{#each content.blocks as block}
  <!-- Inside the content.blocks for loop... -->
  {#if block.type == 'lottie'}
    <ScrollerLottie
      src={`${assets}/animations/${block.src}`}
      theme={block.theme}
      autoplay
      loop
      showDebugInfo
    >
      {#each block.foregrounds as foreground}
        {#if foreground.type == 'text'}
          <ScrollerLottieForeground
            endFrame={parseInt(foreground.endFrame)}
            startFrame={parseInt(foreground.startFrame)}
            text={foreground.foregroundProps.text}
          />
        {:else if foreground.type == 'component'}
          {@const Component =
            Components[foreground.foregroundProps.componentType]}
          <ScrollerLottieForeground
            endFrame={parseInt(foreground.endFrame)}
            startFrame={parseInt(foreground.startFrame)}
          >
            <Component {...foreground.foregroundProps} />
          </ScrollerLottieForeground>
        {/if}
      {/each}
    </ScrollerLottie>
  {/if}
{/each}
```
