Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy loading animation data would be very handy #11

Closed
EliotSlevin opened this issue Nov 26, 2020 · 8 comments
Closed

Lazy loading animation data would be very handy #11

EliotSlevin opened this issue Nov 26, 2020 · 8 comments

Comments

@EliotSlevin
Copy link

This might be far beyond the scope of this library - but being able to load animation data when it enters the viewport would be great.

Including animation data via import is good, but if you've got quite a few complex animations on a page, this can increase your bundle size real fast. Particularly if inline bitmaps are part of an animation. I'm working on a project where our average lottie json is ~1mb, and we've got like 6 per page. That's a chunky bundle! A chunky bundle that's getting in the way of my performance scores!

A lazyload feature, built into a library like this could be hella handy. Something like

<Lottie
    loop
    play
    animationData={'./some/static/path.json'}
    lazyLoad
    placeholder={placeholder.svg}
/>

The Interaction Observer API would be perfect to check if the lottie is in viewport - then if it is, fetch the json and fire up the animation.

Would be very nice.

@mifi
Copy link
Owner

mifi commented Nov 26, 2020

Hi. I think you can solve the bundle size with code splitting:

const MyComponent = () => {
  const [animationData, setAnimationData] = useState();

  useEffect(() => {
    import('./animation.json').then(setAnimationData);
  }, []);

  if (!animationData) return <div>Loading...</div>;
  return <Lottie animationData={animationData} />;
}

See https://reactjs.org/docs/code-splitting.html

Then you can mount this component only when it enters the view. Can be achieved with something like this:
https://github.com/olistic/react-use-visibility

@mifi mifi closed this as completed Jan 24, 2021
@EliotSlevin
Copy link
Author

Just if anybody ends up finding this in the future, I solved this problem just as you suggested, with a component like this.

import React, { useEffect, useState } from 'react'
import Lottie from 'react-lottie-player'

const Animation = ({animationName, placeholder}) => {
    const [animationData, setAnimationData] = useState()
  
    useEffect(() => {
      import(`./lotties/${animationName}`).then(setAnimationData)
    }, [])
  
    if (!animationData) return <img src={placeholder}/>
    return <Lottie animationData={animationData} play loop/>
  }
export default Animation

When the component first mounts, it renders the SVG placeholder I've passed through - all in nice small bundle. At this point the user can start interacting with the page, then it starts loading the animation data, then renders it once it's all available.

I've found that just doing that is enough to dramatically improve the performance score. Detecting what Animations were in the viewport and then loading just those files just wasn't needed for my project.

Thanks for the help and banging library!

@Skarian
Copy link

Skarian commented Jul 3, 2021

Just if anybody ends up finding this in the future, I solved this problem just as you suggested, with a component like this.

import React, { useEffect, useState } from 'react'
import Lottie from 'react-lottie-player'

const Animation = ({animationName, placeholder}) => {
    const [animationData, setAnimationData] = useState()
  
    useEffect(() => {
      import(`./lotties/${animationName}`).then(setAnimationData)
    }, [])
  
    if (!animationData) return <img src={placeholder}/>
    return <Lottie animationData={animationData} play loop/>
  }
export default Animation

When the component first mounts, it renders the SVG placeholder I've passed through - all in nice small bundle. At this point the user can start interacting with the page, then it starts loading the animation data, then renders it once it's all available.

I've found that just doing that is enough to dramatically improve the performance score. Detecting what Animations were in the viewport and then loading just those files just wasn't needed for my project.

Thanks for the help and banging library!

@EliotSlevin Ever encountered this error? Whenever I use your code nothing loads. JSON files work just fine without lazy-loading and I have tried multiple JSONs from LottieFiles

import React, { useEffect, useState } from 'react';
import Lottie from 'react-lottie-player';

const Hero = () => {
  const [animationData, setAnimationData] = useState();

  useEffect(() => {
    import(`../../lottie/woman.json`).then(setAnimationData);
  }, []);

  if (!animationData) return <div>Loading...</div>;
  return <Lottie animationData={animationData} play loop />;
};
export default Hero;

Lottie File

Keep getting SVG errors in console:
image

Edit: Resolved it by using the below

import React, { useEffect, useState } from 'react';
import Lottie from 'react-lottie-player';

const Hero = () => {
  const [animationData, setAnimationData] = useState();

  useEffect(() => {
    import(`../../lottie/woman.json`).then((res) => setAnimationData(res.default));
  }, []);

  if (!animationData) return <div>Loading...</div>;
  return <Lottie animationData={animationData} play loop />;
};
export default Hero;

@nathanielwood
Copy link

I just want to add that it seems the issue @Skarian mentioned above is introduced with v1.3.2. I tried switching back to v1.3.1 and the error was gone. Thanks for the including the code to resolve it!

@mifi
Copy link
Owner

mifi commented Jul 14, 2021

Thanks for reporting @nathanielwood

I can confirm this is an issue with the cloneDeep addition in 1.3.2, I opened #37
This will be fixed in 1.3.3, so no longer need to use .default on imported json after that

@simonpkerr
Copy link
Contributor

simonpkerr commented Jul 14, 2021

maybe use something like merge-anything? that seems pretty stable and doesn't introduce as many security risks as lodash. I'm happy to pr this in if it helps

@mifi
Copy link
Owner

mifi commented Jul 14, 2021

this one? https://github.com/mesqueeb/merge-anything
I believe it is not the same as cloneDeep, or is it? I think lodash.cloneDeep is quite safe as it is just a single function, and it has a lot more downloads/users:

@simonpkerr
Copy link
Contributor

there are high security warnings for anything lodash due to the way it works (https://snyk.io/vuln/SNYK-JS-LODASH-450202). its going to make it harder for consumers if you use lodash, since they will constantly have to do updates. we've found this on virtually every project that uses lodash. if you check out the pr I raised (#40), everything still works fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants