一,原理
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...