airbnb / lottie-android

Render After Effects animations natively on Android and iOS, Web, and React Native
http://airbnb.io/lottie/
Apache License 2.0
35.03k stars 5.41k forks source link

Flicker on animation change in an LottieAnimationView #858

Closed CotunaAurelian closed 6 years ago

CotunaAurelian commented 6 years ago

Hello guys I have a question. I am adding LottieAnimationView to a TabBar. When I select a tab, I have to change the JSON to another one that is coloured (to highlight the selected tab). The animation won't start right away, it will be triggered at some point after an event, so I can't play the animation right after it has been drawn in order to force frame redrawing mechanism.

When I do this, I can see that the LottieAnimationView flickers, and then load the new JSON file with the highlighted image.

I suspect that this is due to a requestLayout call, that will remeasure and redraw the whole LottieAnimationView and when this happens the JSON File is parsed asynchronously.

Is there a way I can switch the animation on the fly without having that weird flicker?

gpeal commented 6 years ago

It probably has to do with the asynchronous parsing of json. You can use LottieComposition.Factory (deprecated and replaced with LottieCompositionFactory in the soon to be released 2.6.0) to parse the composition ahead of time.

CaptainHuangsh commented 5 years ago

Hello. I have the same problem; and use LottieComposition.Factory before playAnimation. It seemed that flicking time is shorter now, but it`s still flicking between two animation.

CaptainHuangsh commented 5 years ago

Hello. I have the same problem; and use LottieComposition.Factory before playAnimation. It seemed that flicking time is shorter now, but it`s still flicking between two animation.

I just update lottie 2.5.5 to 2.7.0 and solve the problem

carstenhag commented 4 years ago

Thanks a lot, this is how I solved this issue on our project:


    private val animationLoadedListener = LottieListener<LottieComposition> { composition ->
        lottie_animation.setComposition(composition)

        // custom method to set repeatcount etc.
        lottie_animation.playFromStartThenLoop()
    }

    private fun setLottieAnimation(index: Int) {
        val task = when (index) {
            0 -> {
                lottie_animation.imageAssetsFolder = "lottie/welcome_1"
                LottieCompositionFactory.fromRawRes(this, R.raw.welcome_1)
            }
            1 -> {
                lottie_animation.imageAssetsFolder = "lottie/welcome_2"
                //lottie_animation.setAnimation(R.raw.welcome_2)
                LottieCompositionFactory.fromRawRes(this, R.raw.welcome_2)
            }
            /// more cases
        }
        task.addListener(animationLoadedListener)
        task.addFailureListener { Timber.e(it) }
    }
StasKorol commented 3 years ago

Have the same issue but your code doesn't seem to work @carstenhag. Maybe, I did it wrong, so could you give some explanation to your code?

carstenhag commented 3 years ago

Sure. Trivially (without making use of tasks/listeners) which is what we did previously, the animation was loaded this way:

  1. Set the image folder
  2. Set the animation directly to the lottie animation view:
    private fun setLottieAnimation(index: Int) {
        when (index) {
            0 -> {
                lottie_animation.imageAssetsFolder = "lottie/welcome_1"
                lottie_animation.setAnimation(R.raw.welcome_1)
            }
    // ...

My guess is that it flickered with that because it first disabled the old animation, then had to load the new animation from disk and could only then start playing the 2nd animation.

So the code from https://github.com/airbnb/lottie-android/issues/858#issuecomment-634673371 loads the new animation files asynchronously with LottieCompositionFactory.fromRawRes and task.addListener(animationLoadedListener). In the animationLoadedListener we then assign the loaded animation to the Lottie Animation View. Thus, it won't flicker.

StasKorol commented 3 years ago

@carstenhag, where should you call "setLottieAnimation()"?

carstenhag commented 3 years ago

On your on tap click listener, or on any other type of listener which provides you the index of the animation or the animation directly.

DebackerK commented 2 years ago

I changed the setAnimation to setComposition so that it preloads the lottie files before actually showing them.

so instead of:

setAnimation(R.raw.animation) use:

LottieCompositionFactory.fromRawResSync(requireContext(), R.raw.animation)?.let { result ->
                                result.value?.let { composition -> setComposition(composition) }
                            }

That stopped the flickering for me. I also used an AnimationListener for the loop.

 private fun setLottieAnimation(lottieView: LottieAnimationView) {
            lottieView.apply {
                var firstAnimation = true
                val loopListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() {
                    override fun onAnimationRepeat(animation: Animator?) {
                        if (firstAnimation) {
                            firstAnimation = false
                            LottieCompositionFactory.fromRawResSync(requireContext(), R.raw.animation_2)?.let { result ->
                                result.value?.let { it -> setComposition(it) }
                            }

                            repeatCount = LottieDrawable.INFINITE
                            playAnimation()
                        }
                    }
                }

                LottieCompositionFactory.fromRawResSync(requireContext(), R.raw.animation_1)?.let { result ->
                    result.value?.let { it -> setComposition(it) }
                }

                repeatCount = 1
                addAnimatorListener(loopListener)
            }
        }
androidfans commented 1 year ago

Can lottie keep last frame on the screen when loading new animation?