250张png图片实现动画实现方案事件

Posted 胖鹅68

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了250张png图片实现动画实现方案事件相关的知识,希望对你有一定的参考价值。

文章目录

一、文章参考

  1. requestAnimationFrame MDN
  2. CSS pointer-events 属性

二、需求说明

最近开发的一个项目中,要求使用一个动画效果,UCD 提供了三种解决方案,各有优劣。

2.1 使用 SVG 做成动画

使用 AE插件 lottie-web 生成一个SVG 动画,浏览器渲染SVG,由于DOM频繁的变动,即不断的重绘,这个动作会大量的占用CPU 和 GPU 的资源,针对一些比较老的机器(工厂、政府单位的机器可能比较老旧),可能会导致资源不够,打开浏览器CPU直接冲到100%导致浏览器卡死。

应用场景:针对特定性能比较强悍的机器,适用于全屏和局部界面的动画

2.2 使用 视屏或者gif作为 动图背景

视频或者gif 能很好的展示动画的完整性,资源占用也比较小,但是会有如下缺点:

  1. 最重要的是不透明, 无法和其他界面有效的整合展示
  2. 无法准确的控制视频的进度

应用场景:可以用于浏览器部分界面的动画

2.3 使用图片来做成动画

原理:人的视觉保留,1秒钟切换24张画面,人就感觉是一个动图

优点:

  1. 图片是透明的,可以作为背景与其他的界面有效结合

缺点:

  1. 图片的数量比较多,需要一次性加载很多图片(浏览器默认在同一域名下,最多发送6个请求连接),效率低
  2. 下载到硬盘的图片导入到内存也需要一定的时间,如果图片数量多,消耗的时间也很多
  3. 将图片实现动画效果,需要的图片大小 要 远大于视频、SVG、gif 图片
  4. 如果动画频率比较高,即1秒钟需要处理的图片很多,比较耗 GPU 和 CPU资源

使用场景:可以作为全屏的解决方案

三、动画解决方案整理

由于UCD设计的动画是全屏的(要透明效果,放弃了gif和视频),但无法确保用户最终使用的设备是高性能的(放弃了SVG方案),最终只能选择 多张图片快速切换实现的动画效果。

3.1 requestAnimationFrame

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

3.1.1 代码实现

在浏览器重绘的时候,修改目标控件的背景图片

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<style>
  .donghuabg
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
    background-image: url('./donghua/y_00000.png');
    background-size: cover;
    pointer-events: none;
  
</style>
<body>
  <div class="donghuabg" id="donghuabg"></div>
</body>
<script>
  const donghua = document.getElementById('donghuabg')

  let num = 0
  let numStr = '000'
  function step() 
    console.log('继续调用' + num);
    if (num < 249) 
      num++
      if (num <= 9) 
        numStr = '00' + num
       else if (num > 9 && num < 100)
        numStr = '0' + num
       else 
        numStr = '' + num
      

      donghua.style.backgroundImage = `url('./donghua/y_00$numStr.png')`
      console.log('继续调用');
      window.requestAnimationFrame(step);
     else 
      console.log('结束')
    
  

  window.requestAnimationFrame(step);
</script>
</html>

3.1.2 现象

浏览器会一直出现白屏现象,一直到动画执行完成,呈现出最后状态的背景图片

3.1.3 原因分析

  1. 普通PC机器显示器是1秒钟刷新60次,即1秒钟会执行60次 requestAnimationFrame 函数
  2. 代码逻辑即 1秒钟会执行60次 切换背景图片,即会下载60张背景图片,然后将60张图片渲染出来,即16.66666 ms(1000 / 60)(毫秒)需要下载完图片并将图片渲染出来,这个依赖于网络和CPU(GPU)资源
  3. 网络保证图片能下载下来,硬件资源(CPU和GPU)保证图片能渲染出来,两个动作需要在16.66666毫秒内完成,由于本机不具备这样的条件和能力,因此图片还没有渲染出来就切换到下一张图片了,导致界面一直处于白屏的状态。

3.2setTimeout 结合

由于动画是10秒钟要执行250张动画,即每张图片的间隔是40ms,因此可以尝试使用setTimeout定时处理

3.2.3 代码实现

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<style>
  .donghuabg
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
    background-image: url('./donghua/y_00000.png');
    background-size: cover;
    pointer-events: none;
  
</style>
<body>
  <div class="donghuabg" id="donghuabg"></div>
</body>
<script>
  const donghua = document.getElementById('donghuabg')

  let num = 0
  let numStr = '000'
  function step() 
    console.log('继续调用' + num);
    if (num < 249) 
      num++
      if (num <= 9) 
        numStr = '00' + num
       else if (num > 9 && num < 100)
        numStr = '0' + num
       else 
        numStr = '' + num
      

      donghua.style.backgroundImage = `url('./donghua/y_00$numStr.png')`
      console.log('继续调用');
      setTimeout(function () 
        step()
      , 40)
     else 
      console.log('结束')
    
  

  setTimeout(function () 
    step()
  , 40)
</script>
</html>

3.2.3 现象

屏幕会不团的闪烁,出现“闪屏”现象

3.2.3 原因分析

每隔40毫秒再次改变控件的背景,即改变的一瞬间,浏览器需要去从网上下载,图片还没有下载到本地,浏览器本地还没有图片渲染,界面就会白屏

3.3 requestAnimationFrame 和 setTimeout 结合

考虑到 requestAnimationFrame 每次都是屏幕刷新的时候才会触发方法,即,图片变化一定是显示器刷新的时候,如果图片每次都能顺利呈现出来,即动画可以顺利完成,因此需要保证刷新的时候,图片是一定下载完成到本地了的。
由于是10秒钟250张图片,因此40ms才会跳转到下一章,这个时间作为网络下载图片和渲染图片的时间。
另外,为了节省网络下载的时间,因此决定先下载图片到本地缓存起来,40ms只用来做为渲染的时间,提高CPU的利用率。

3.2.1 代码实现

用5秒钟先下载图片,5秒之后,再开始切换图片的逻辑

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<style>
  .donghuabg
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
    background-image: url('./donghua/y_00000.png');
    background-size: cover;
    pointer-events: none;
  
</style>
<body>
  <div class="donghuabg" id="donghuabg"></div>
  <div id="aaa">
    <!-- <img src="./donghua/y_00000.png" /> -->
  </div>
</body>
<script>

  function loadImg()
    let target = ''
    let numStr = ''
    let num = -1
    for (var i = 0; i < 250; i++) 
      num++
      if (num <= 9) 
        numStr = '00' + num
       else if (num > 9 && num < 100)
        numStr = '0' + num
       else 
        numStr = '' + num
      
      target = target + `<img src="./donghua/y_00$numStr.png" style="display:none" />`
    
    console.log(target);
    document.getElementById('aaa').innerHTML = target
  
  loadImg()

  setTimeout(function () 
    const donghua = document.getElementById('donghuabg')
    let num = -1
    let numStr = '000'
    let startTimeStamp = 0
    let currentTimeStamp = 0
    function step() 
      console.log('继续调用' + num);
      if (startTimeStamp === 0)  // 表示是第一次开始遍历
        num++
        startTimeStamp = new Date().getTime()
        donghua.style.backgroundImage = `url('./donghua/y_00$numStr.png')`
        window.requestAnimationFrame(step);
       else  // 表示不是第一次遍历了
        currentTimeStamp = new Date().getTime()
        console.log((currentTimeStamp - startTimeStamp) > 40);
        if ((currentTimeStamp - startTimeStamp) > 40 ) 
          startTimeStamp = currentTimeStamp
          if (num < 249) 
            num++
            if (num <= 9) 
              numStr = '00' + num
             else if (num > 9 && num < 100)
              numStr = '0' + num
             else 
              numStr = '' + num
            

            donghua.style.backgroundImage = `url('./donghua/y_00$numStr.png')`
            console.log('继续调用');
            window.requestAnimationFrame(step);
           else 
            console.log('结束')
          
         else  // 时间间隔相差不到40毫秒
          window.requestAnimationFrame(step);
        
      
    

    window.requestAnimationFrame(step);
  , 5000)

</script>
</html>	

3.3.2 现象

使用5秒预先下载图片之后再执行动画脚本:动画执行比较流畅

不使用5秒下载图片,直接执行动画脚本:浏览器会出现闪屏的情况

3.3.3 原因分析

不使用5秒下载图片,直接执行动画脚本:
由于两张图片之间的时间间隔是40ms,需要同时完成下载和展示,才能保证动画的连贯性。下载的时间要远大于CPU渲染图片的时间,如果下载的时间比较长,而背景已经变化,但是还没有下载下来,就会出现白屏,一旦下载了,就立马渲染出来,整个过程就会出现“白屏-渲染背景-白屏-渲染背景”的往复循环,即“闪动”现象。

使用5秒下载图片之后再执行动画脚本:
浏览器提前会下载图片并缓存到本地,执行动画的时候,下载过程就变成了从缓存获取,大大缩短了下载图片的时间,40ms的时间留有足够的时间用于渲染,看上去就比较流畅

3.4 雪碧图

雪碧图:将多个图片合并为一张图片
动画原理:将雪碧图作为div 控件的背景,**然后定时器改变背景图的展示位置,在1秒钟内变换的次数超过24次,**人就会感觉是动画的效果

优点:

  1. 只用发送一次请求,即获得所有的图片,提高请求的效率
  2. 只需要切换背景的位置,而不用切换图片资源(重新下载图片),只会重绘不会回流,提高效率

3.4.1 代码实现

由于有250张图片,如果合成为1张图片,太大了,不确定能否显示出来,而且不方便整合。因此,UCD给了我10张图片合并为1张雪碧图,在雪碧图从第一张切换到最后一张背景,然后切换到下一个雪碧图,依次走到最后一张图的背景切换

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<style>
  .donghuabg
    position: absolute;
    left: 0;
    top: 0;
    height: 1080px;
    width: 1920px;
    /* background-image: url('./xuebi/00-09@1x.png'); */
    /* background-size: cover; */
    pointer-events: none;
    /* background-repeat: no-repeat; */
  
</style>
<body>

  <div class="donghuabg" id="donghuabg"></div>
  <div class="aaa" id="aaa"></div>
</body>
<script>
   function loadImg()
    let target = ''
    let numStr = ''
    let num = -1
    for (var i = 0; i < 5; i++) 
      num++
      // if (num <= 9) 
      //   numStr = '00' + num
      //  else if (num > 9 && num < 100)
      //   numStr = '0' + num
      //  else 
      //   numStr = '' + num
      // 
      target = target + `<img src="./xuebi/$num0-$num9@1x.png" style="display:none" />`
    
    console.log(target);
    document.getElementById('aaa').innerHTML = target
  
  loadImg()

  setTimeout(function () 
    const donghua = document.getElementById('donghuabg')
    let num = -1 // 图片的张数
    let bgPositionIndex = -1 // 图片背景的序号
    let numStr = '000'
    let startTimeStamp = 0
    let currentTimeStamp = 0
    function step() 
      console.log('继续调用' + num);
      if (startTimeStamp === 0)  // 表示是第一次开始遍历
        num++
        bgPositionIndex++
        startTimeStamp = new Date()<

以上是关于250张png图片实现动画实现方案事件的主要内容,如果未能解决你的问题,请参考以下文章

250张png图片实现动画实现方案事件

PNG的精灵动画[颤振]

CSS3实现的线条波浪动画效果

CSS3实现的线条波浪动画效果

Android帧动画实现,防OOM,比原生动画集节约超过十倍的资源

基于原生js的图片轮播效果简单实现