记录lottie动画踩坑(内存泄漏、帧误差、销毁方式)

2,072 阅读2分钟

1. 内存泄露

  • 我测试的是AE文件导出的lottie文件(单个动画对象包含3个蒙版,3-5个元素,无渐变颜色,动画时长1s。一共有3-4个这样的动画对象),这样的动画如果不对lottie生成的对象进行销毁的话,经过测试存在内存泄露问题,不使用缓存机制,会导致长时间使用下,内存占用持续走高:

image.png

  • 这里使用单实例模式,缓存anim对象的引用,可以平衡内存的使用。
    // Singleton
    if (anim) {
      anim.doSth();
    } else {
      anim = lottie.loadAnimation(data);
    }
    
  • 结果如图,没有内存占用持续走高的现象发生:

image.png

2. 帧误差

  • 在调用, anim.play()方法后,动画会从第0帧执行到最后一帧停止,正常情况下是不会出问题的。
  • 但是在暴力测试下,如果频繁触发动画执行,会导致终点动画的结束帧有误差,比如我做的60帧的动画,在暴力测试下,会导致终点帧为59帧,如图:

image.png

  • 这样的话,下次再执行anim.play方法时,是从第59帧执行到60帧,而不是从第0帧执行到第60帧(即replay),所以这里需要定义一个方法使其replay。
    const replay = (anim: AnimationItem) => {
      if (anim.currentFrame !== anim.totalFrames) anim.goToAndStop(anim.totalFrames, true);
      anim.play();
    };
    

3. 销毁方式

  • 这里在销毁的地方,effect hook上踩了一个坑,location会因为路由变化,重新触发effect hook。
  • 这里需要注意,如果一个effect同时包含return函数和refs的时候,如果refs变化,那么这时return函数会执行,以清除上一次effect。
  • 所以因为location的变化,在当前这个effect hook里,我把anim给销毁了,那么就会存在一些判断逻辑上的缺漏!!
  • 所以最好的hook使用方式,应该是将会有相互影响的hook分开来写,创建/销毁应该清晰化去处理,这样才不会产生连串效应。
  const location = useLocation(); // react router dom
  
  // wrong way
  useEffect(() => {
    // add event listener on location's change events
    // ... deal events
    return () => {
      anim && anim.destroy()
    }
  }, [location])
  
  // correct way
  useEffect(() => {
    // add event listener on location's change events
    // ... deal events
  }, [location])
  
  useEffect(() => {
    return () => {
      anim && anim.destroy()
    }
  }, [])

4. 结束语

  • 如果觉得写的不错,有用的话,还请帮我点个赞 O(∩_∩)O
  • 转发请注明出处,谢谢!