js控制gif动画帧,基于libgif.js实现

一,原理

1.canvas、img、video,都是图像对象,属于图像类型的节点。这种图像对象不是Image 对象,而是对加载图像数据流的节点的统称。

  • img 里的图像数据来源于提前制作好图片文件的,是静止的,比如.jpg,.png,.svg 等。.gif 是个特殊存在,另当别论。
  • vedio 里的视频数据来源于提前制作好的视频文件,是动起来的。
  • canvas 图像数据是动态生成的。

2.图像对象的数据的读写

  • 读取图像数据

    • img 和video 没有直接获取ImageData 的方法,但可以通过canvas 获取
    • canvas 使用getImageData 方法读取自身的图像数据
  • 写入图像数据

    • img 和video 只能通过src ,以资源路径的方式设置其显示图像(img 的src 还可以使用base64)。但他们无法直接用ImageData 设置图像。
    • canvas 可以用drawImage 或fillStyle 方法,以图像对象为参数,为canvas 或其内部元素写入图像数据。canvas 也可以通过putImageData方法,以ImageData 对象为参数,为其写入图像数据。
    • 3.gif 是介于图片和视频之间的特殊存在。
  • 用读取image 数据的原理读取它,只能读到第一帧gif 图片。

直接绘制gif 肯定是不好使的,这里就用到了一个插件libgif.js

二,插件libgif.js

libgif.js已经完成了对gif 的解析,并将其写入了canvas 中。

libgif.js网址:github.com/buzzfeed/l....

下面是我从官网上复制粘贴下来,用百度翻译一下 ,然后略作调整。英语好的可以跳过这里,看官网。

1.写在图片标签里的属性 Image tag attributes

  • rel:animated_src - 如果指定了此url,则将其加载到播放器而不是src中。这允许显示预览帧,直到动画gif数据流入画布
  • rel:auto_play - 如果未指定,则默认为1。如果设置为零,则需要调用play()方法
  • rel:rubbable - 如果未指定,则默认为0。如果设置为1,则gif将是一个带有处理程序的画布来处理摩擦。

2.构造函数 Constructor options

  • gif - 必填。img标签的DOM元素。
  • loop_mode - 可选。将此设置为false将强制禁用gif的循环。
  • auto_play - 可选。与上面的rel:auto_play属性相同,此arg会覆盖img标记信息。
  • max_width - 可选。将图像从max_width缩放到max_width。有助于移动。
  • rubbable - 可选。让它可以擦掉。
  • on_end - 可选。添加一个回调,用于当gif到达单个循环结束时(一次迭代)。传递的第一个参数将是gif HTMLElement。
  • loop_delay - 可选。每次循环(迭代)后暂停的时间(以毫秒为单位)。
  • progressbar_height - 可选。进度条的高度。
  • progressbar_background_color - 可选。进度条的背景颜色。
  • progressbar_foreground_color - 可选。进度条的前景色。

3.loading 事件

  • load(callback) - 将src指定的gif或img标记的rel:animated_src sttributie加载到canvas元素中,然后调用callback(如果有的话)
  • load_url(src,callback) - 将src参数中指定的gif文件加载到canvas元素中,如果传递了一个,则调用callback

4.播放控制器 play controls

  • play - 开始玩GIF
  • pause - 停止播放gif
  • move_to(i) - 移动到gif的第i帧
  • move_relative(i) - 向前移动i帧(如果i <0则向后移动)

5.数据获取 getters

  • get_canvas - gif正在播放的canvas元素。方便分配事件处理程序。
  • get_playing - gif当前是否正在播放
  • get_loading - gif是否已完成加载/解析
  • get_auto_play - 是否将gif设置为自动播放
  • get_length - gif中的帧数
  • get_current_frame - 当前显示的gif帧的索引

6.官方案例

xml 复制代码
<center>
	<h1>控制gif播放/暂停/快进快退</h1>
	<img id="example1" src="./example_gifs/rub_test_preview.jpg" rel:animated_src="./example_gifs/rub_test.gif" rel:auto_play="0" width="467" height="375" />
	<br>
	<script type="text/javascript">
		var sup1 = new SuperGif({ gif: document.getElementById('example1') } );
		sup1.load();
	</script>
	<a href="javascript:;" onmousedown="sup1.play(); return false;">播放</a> |
	<a href="javascript:;" onmousedown="sup1.pause(); return false;">暂停</a>
	<a href="javascript:;" onmousedown="sup1.move_to(0); return false;">还原</a> |
	<a href="javascript:;" onmousedown="sup1.move_relative(1); return false;">下一帧</a> |
	<a href="javascript:;" onmousedown="sup1.move_relative(-1); return false;">上一帧</a>
</center>                
                
<img src="./example1_preview.gif" rel:animated_src="./example1.gif"
width="360" height="360" rel:auto_play="1" rel:rubbable="1" />

<script type="text/javascript">
        $$('img').each(function (img_tag) {
                if (/.*\.gif/.test(img_tag.src)) {
                        var rub = new SuperGif({ gif: img_tag } );
                        rub.load(function(){
                                console.log('oh hey, now the gif is loaded');
                        });
                }
        });
</script>

提示:

  • RubbableGif 对象继承自 SuperGif 对象,SuperGif是解析gif 的核心。
  • 前端建立的 节点,只是一个向SuperGif 对象传递数据的工具,等SuperGif 解析完了gif,就会用canvas 将img 替换掉。

三,实际应用

前景是这样的,用gif做一个进度条,等待gif动画播放完成后,显示按钮;

一开始我是用了一个定时器设置时间跟gif动画时间一样长,时间到动画播放完成,按钮显示,没毛病; 但是,在实际运用过程中发现,加载gif由于网络原因跟定时器就不会同步,很多情况是,定时器走完了,gif才播放一半,显然效果不理想,后来经过产品提醒,能不能判断gif播放完毕后,再显示这个按钮;于是查找资料,发现了这个libgif.js插件挺好的,于是做了尝试,完美解决问题,在此记录一下。

本文重点讲述运用libgif.js实现,加载一个load.gif完成后,进行下一步的业务逻辑,代码如下:

xml 复制代码
<!--样式-->
<style type="text/css">
*{margin: 0; padding: 0;}
.load_p{ position: relative; width: 100vw; height: 100vh; }
a.play_2{display: none; position: absolute; left: 50%; top: 50%; margin-left:-370px; margin-top:-37px;}
</style>
xml 复制代码
<!--dom结构-->
<script type="text/javascript" src="./libgif.js"></script>
<div class="load_p">
	<img id="load" src="./example_gifs/loadbg.gif" rel:animated_src="./example_gifs/loadbg.gif" rel:auto_play="0"/>	
	<a href="javascript://" class="play_2" id="btn"><img src="./example_gifs/p1_btn_v.png" alt=""/></a>  
</div>
xml 复制代码
<!--js-->
<script type="text/javascript">
var gif = new SuperGif({ 
	gif: document.getElementById('load'), //img标签的DOM元素。
	loop_mode:false, //将此设置为false将强制禁用gif的循环。
	progressbar_foreground_color: "rgba(255,255,255,0.1)",
    progressbar_background_color: "rgba(255,255,255,0.1)",
    progressbar_height: 1
});
gif.load(function() {
    var lastFrameIndex = gif.get_length() - 1; // 获取最后一帧的索引
    gif.play(); 

    // 设置一个定时器,周期性检查当前帧
    var timer = setInterval(function() {
        if (gif.get_current_frame() === lastFrameIndex) {
            console.log("播放结束");
            document.getElementById('btn').style.display='block';
            clearInterval(timer); // 清除定时器            
        }
    }, 100); 
});
</script>

最终效果,是loadbg.gif动画播放完成后,a.play_2按钮显示。

附上libgif.js github:github.com/buzzfeed/li...

相关推荐
Myli_ing12 分钟前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风15 分钟前
前端 vue 如何区分开发环境
前端·javascript·vue.js
PandaCave21 分钟前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
软件小伟24 分钟前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾1 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧1 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
疯狂的沙粒2 小时前
对 TypeScript 中高级类型的理解?应该在哪些方面可以更好的使用!
前端·javascript·typescript
旭日猎鹰3 小时前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
一条晒干的咸魚3 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
花海少爷3 小时前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript