dotlottie / player-component

https://dotlottie.io
MIT License
207 stars 28 forks source link

autoplay and loop don't seem to work in svelte #246

Open rjwalters opened 1 year ago

rjwalters commented 1 year ago

I am trying to migrate from @lottiefiles/lottie-player but I am having trouble getting autoplay to work...

<script lang="ts">
  import { onMount } from 'svelte';
  let lottiePlayerReady = false;
  onMount(async () => {
    await import('@dotlottie/player-component');
    lottiePlayerReady = true;
  });
  let lottieSrc =
    'https://lottie.host/13489d72-9fb8-47e8-94b9-42d1c880647b/zMLtJHw8sJ.json';
</script>

{#if lottiePlayerReady}
  <dotlottie-player
    src={lottieSrc}
    autoplay
    loop
    style="width: 500px"
  ></dotlottie-player>
{/if}

if I include the controls, I am able to play and loop the lottie. A workaround is to trigger play when I receive the ready event:

  onMount(async () => {
    await import('@dotlottie/player-component');
    lottiePlayerReady = true;
    await tick();
    const lottiePlayer = document.querySelector('dotlottie-player');
    lottiePlayer.addEventListener('ready', () => {
      (lottiePlayer as any).play();
    });
  });
dhruv-m1 commented 1 year ago

Faced the same issue (using Svelte island on Astro), thanks for the workaround @rjwalters

The problem seems to be with autoplay. (Edit: loop works on an Astro Island using Svelte, but not with Svelte alone)

If anyone else has multiple animations to deal with, you can extract dotlottie-player into a separate svelte component like so:

<script>

    import { onMount } from 'svelte';

    export let play = true;
    export let loop = true;
    export let mode = 'normal';
    export let controls = false;
    export let src;

    let player;
    onMount(async() => {
        player.addEventListener('ready', () => {
            if (play) player.play();
            player.setLooping(loop);
        });
    });

</script>

<dotlottie-player bind:this={player}

    src = {src}
    mode = {mode}
    controls = {controls}
    worker = true
>
</dotlottie-player>

Edit: Just to add this issue was not there in version 1.4.2.

Djules commented 1 year ago

Hi, I faced the same issues in Vue 3 project, the attributes were handled as prop instead of attribute, although the <dotlottie-player> element was declared as CustomElement.

I don't know Svelte (yet) but I figured this out by "forcing" Vue to use attributes as they are, with .attr modifier, something like:

<dotlottie-player src="/lottie/loader.lottie" :autoplay.attr="true" :loop.attr="true" />
Djules commented 1 year ago

Maybe some solutions for you in this article.

theashraf commented 1 year ago

@rjwalters We're investigating this issue. Thank you.

@Djules Regarding the Vue usage, you can find a guide here that might help: https://docs.lottiefiles.com/dotlottie-players/components/player-component/usage/vue

GregSharp-app commented 1 year ago

This fixed it,

<script>
function setAttrLottie(node) {
    node.setAttribute('autoplay', 'true');
    node.setAttribute('loop', 'true');
}
</script>

<dotlottie-player
    use:setAttrLottie
    mode="normal"_
    src="..."
/>
dhruv-m1 commented 1 year ago

Yes, @GregSharp-app - that's the current workaround.

If you have multiple animations with different requirements for autoplay and loop, you can use this more generic function:

<script>
  const setLottieConfig = (player, config) => {
    Object.entries(config).forEach(([key, value]) => {
      player.setAttribute(key, value);
    });
  };
</script>

  <!-- 

    setting autoplay, loop to false does not work,
    don't include them at all if you want them to be false.

  -->

  <dotlottie-player 

    use:setLottieConfig = {{
      autoplay: true,
      loop: true,
      mode: 'normal',
      src: "...",
    }}

  />

This involves DOM manipulation, which might lead to some performance issues if you have a lot of animations.

If you don't want to do DOM manipulation, you can make a component for lottie animations like so:

<script>

    import { onMount } from 'svelte';

    export let play = true;
    export let loop = true;
    export let mode = 'normal';
    export let controls = false;
    export let src;

    let player;
    onMount(async() => {
        player.addEventListener('ready', () => {
            if (play) player.play();
            player.setLooping(loop);
        });
    });

</script>

<dotlottie-player bind:this={player}

    src = {src}
    mode = {mode}
    controls = {controls}
>