最近碰到了一个需求, 大致是移动端有一个提示页, 在页面中会四个GIF图, 连起来像一个”小视频”一样, 用来展示商品的步骤.
但是四个GIF一起播放的话, 那么用户体验就自然没有那么好啦. 我仔细的想了想, 想到了微博的GIF图好像就是一张一张播放的, 那么我们前端有没有办法也实现这个逐个播放的功能呢…
事实上, 浏览器并没有给我们提供控制GIF的API(据说曾经好像有, 但因为用户体验的问题被废除了, 关于这点我没有去考证过), 我们无法得知这个动画是否已经结束了, 或者控制它的播放和停止.
网上并没有太多关于这方面的资料, 不过张鑫旭dalao的这篇文章. 其中一个方法给我一个思路 —— 虽然img
并没有这种事件, 但是我们可以使用canvas做替换呀.
说干就干, 这里借鉴dalao的代码, 对HTMLImageElement
(可以用来操纵<img>
元素的布局和图像)的原型作扩展, 增加两个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| if ('getContext' in document.createElement('canvas')) {
HTMLImageElement.prototype.play = function () { if (this.storeCanvas) { this.storeCanvas.parentElement.removeChild(this.storeCanvas); this.storeCanvas = null; this.style.opacity = ''; } if (this.storeUrl) { this.src = this.storeUrl; } };
HTMLImageElement.prototype.stop = function () {
const canvas = document.createElement('canvas'); let width = this.width; let height = this.height;
if (width && height) { if (!this.storeUrl) { this.storeUrl = this.src; }
canvas.width = width; canvas.height = height; canvas.getContext('2d').drawImage(this, 0, 0, width, height);
try { this.src = canvas.toDataURL("image/gif"); } catch (e) { this.removeAttribute('src'); canvas.style.position = 'absolute';
this.parentElement.insertBefore(canvas, this); this.style.opacity = '0'; this.storeCanvas = canvas; } } }; }
|
因为前台并没有我们想要操作图片的事件, 因此无法得知gif能持续多少秒, 这点只能由服务端来判断. 所幸这次情况没有那么复杂, 我们不需要适配随机的GIF. 就根据我们手头的动图计算有多少帧, 查看得知每个GIF播放时间都为3s. 再使用定时器的方式去调用方法, 为了防止用户没看清GIF的动作, 因此在定时器时间上再翻了一倍.
页面代码大致如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <div class="body"> <h3 class="body-title">观影指南</h3> <div class="body-tips"> <div class="tips-line clearfix"> <div class="tips-group"> <div class="tips-img"> <img src="./tips-1.gif" alt="tips-1"> <span class="tips-bar"></span> </div> <p>1.坐上座椅,系好安全带</p> </div> <div class="tips-group"> <div class="tips-img"> <img src="./tips-2.gif" alt="tips-2"> <span class="tips-bar"></span> </div> <p>2.于右手边取眼镜佩戴,并带上耳机</p> </div> </div> <div class="tips-line clearfix"> <div class="tips-group"> <div class="tips-img"> <img src="./tips-3.gif" alt="tips-3"> <span class="tips-bar"></span> </div> <p>3.按下扶手上的按钮,开始观影</p> </div> <div class="tips-group"> <div class="tips-img"> <img src="./tips-4.gif" alt="tips-4"> <span class="tips-bar"></span> </div> <p>4.如感不适,长按按钮停止观 影</p> </div> </div> </div> </div>
|
首先选择全部目标GIF, 使其暂停(初始化). 紧接着包装一下定时器用函数调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const images = document.querySelectorAll('.tips-img img') let palyTimer = null;
function palyGif (num = 0) { clearTimeout(palyTimer) images[num].play()
palyTimer = setTimeout(() => { images[num].stop() num = (images.length - 1 <= num) ? 0 : ++num images[num].play() return palyGif(num) }, 6000); }
images.forEach(img => img.addEventListener('load', img.stop, { once: true }))
setTimeout(() => palyGif(), 50);
|
仅仅几行代码留实现我们想要的效果啦(图片压了下):
至于微博那种逐个播放的效果, 我原本想在控制台研究一下它实现的原理. 但仔细一看, 发现微博动图在手机客户端和非客户端上的效果是不一样的. 也就是说在安卓客户端上的确逐个播放, 但是在手机网页上却是一起播放, 并没有实现这个功能, PC页面同理, 因此推测并不是使用js实现的.
最后各位看官如果有什么好的想法的话, 可以留个言一起交流一下呗~