HTML img tag 的进化:不是 GIF 动图,胜过 GIF 动图
概要
- GIF 确实很好,但是却有着严重的图像质量损失和性能损耗。
- 以
<video>
替换 GIF 效果更好,但是会造成性能缺陷: 无法预加载,需要范围请求。 - 现在可以在 Safari Technology Preview 中使用
<img src=".mp4">
标签了。 - 初步测试结果显示,
<img>
标签中的 MP4 比同等条件下的 GIF 播放快 20 倍、解码快 7 倍——而文件尺寸仅为 1/14 ! - 背景 CSS 视频和响应式视频(Responsive Video )终于可以独立为一个“东西”了。
- 动态照片(cinemagraphs)终于摆脱了 GIF 的固有弊病。
- 很期待其他浏览器快速跟进:本文在 Chrome 中打开有 46 MB,但是在 Safari Technology Preview 中只有 2 MB。
特别致谢:Eric Portis, Jer Noble, Jon Davis, Doron Sherman, 和 Yoav Weiss.
引言
很多人对 GIF 都是既爱又恨。
Safari Tech Preview 的出现可能会改变这一切,让人们对 GIF 全都是爱。
实际上,动画 GIF 的用法脱离了 GIF 的本意,比如最初的 GIF89a 声明指出:
GIF 格式(Graphics Interchange Format)并非为动画设计的平台,尽管可以在有限程度上如此操作。
但是 GIF 却发展成了动态照片、表情包和创意展示的绝佳工具。当然了,这些绝佳应用都是有代价的。动画 GIF 的 web 端性能十分差劲:GIF 体积庞大、耗费移动蜂窝流量、需要更多的 CPU 和内存、导致重绘并严重降低电池性能。一般而言,GIF 是 H.264 视频体积的 12 倍,在浏览器中的加载和播放要消耗 2 倍的电量。结果,消耗如此多的资源,得到的效果却不尽人意——GIF 的 256 色的限制导致很多 GIF 特别难看(尽管有一些看起来很棒)。
小孩子通常都会很喜欢 GIF ——但是他们却不会明白,为什么用来看 GIF 的设备的电池总是很快就没电。
GIF 有很多优势:迅速响应浏览器的请求、自动播放而且循环播放、没有声音。这也意味着 GIF 会比较简短。市场调研表明,用户更喜欢一分钟以内的微视频和动态图片,而不是很长的视频和静态图片。从这里来看,GIF 对于用户体验是很好的。
如何让人们从对 GIF 既爱且恨转变为十分喜爱 Gif 的呢?(这里为了区分而故意改变了大小写)。
在最新的 Safari Tech Preview 中,多亏了Jer Noble 的辛苦工作,人们可以直接在<img>
标签中加载 MP4 文件了:这个用例不是很长的视频,而是简短的、无声的、循环播放的视频,就像 GIF 一样。你可以自己先看一下:
<img src="rocky.mp4">
这个效果很棒。这对于企业和个人用户,尤其是就 Web 性能而言,都是很了不起的。
但是我们已经有 <video >
标签了啊?
正如很多人指出的那样,直接采用 <video >
标签会比使用 GIF 的性能要好得多。这也是 Twitter 在 2014 年宣布增加对动画 GIF 的支持而不增加对 GIF 的支持的原因。Twitter 将 GIF 转换为 MP4 文件,并通过 <video >
标签进行传输。因为所有的浏览器都支持 H.264 视频格式,所以这个转换很简单。
<video autoplay loop muted inline>
<source src="eye-of-the-tiger-video.webm" type="video/webm">
<source src="eye-of-the-tiger-video.mp4" type="video/mp4">
<img src="eye-of-the-tiger-fallback.gif"/>
</video>
将动画 GIF 转换为 MP4 十分简单,只需要运行一行命令就行可以:ffmpeg -i source.gif output.mp4
。
然而,并不是所有人都能拆解自己的内容管理系统并将 <img>
转换为 <video >
标签。就算你可以做到,这里也存在 3 个问题
1. 浏览器处理 <video >
标签的速度很慢
正如 Goug Sillars 最近在 HTTP Archive 的一篇文章指出,使用 <video >
标签会造成很大的性能损耗。
与 <img>
标签不同的是,浏览器不会预先加载 <video>
标签内容。一般而言,浏览器只会预加载 JS、CSS 和图像资源,因为他们对于页面展示是不可或缺的。由于 <video>
标签的内容尺寸不一,所以 <video>
标签是在主线程开始准备展示内容的时候才会加载的。这样就导致 <video>
标签加载延迟数百微秒。
例如,速度峰会页面(Velocity conference page)上方的视频需要 5 秒才能加载,而他是此页面中的第 27 个加载项,是在 Start Render 请求之后,也在网页字体加载之后。
更糟糕的是,很多浏览器认为 <video>
标签包含着很长格式的内容。为了防止在不想看完视频的情况下浪费移动流量,这些视频不是一次性加载完毕的。浏览器会首先执行一个 1 比特的请求,以测试服务器是否支持HTTP 范围请求。然后,会有一系列不同大小的范围请求,以保证视频得到足够而不过分的缓冲。这样的结果就是,在浏览器开始解码内容以前就发生了多个 TCP 往返循环,显著延迟了用户看到视频的进程。在移动网络有较高延迟的情况下,这些往返循环可能导致视频播放有上百甚至上千微秒的延迟。
典型的 JS 视频播放器比本地的 <video>
标签表现更差劲。通常将视频嵌入网站的最简单的方法是利用本地化服务,就像 YouTube 和 Vimeo 做的那样,这样能够避免视频解码、播放和 UX 等复杂工作。这个方法很好,但是对于微视频或者像前述网站顶部的关键内容来说,只是徒增延迟而已——原因不外乎是 JS 播放器和这些本地服务需要注入的支撑资源(css/js/jpg/woff)。在 <video>
之外,浏览器不得不下载、评估和执行 JS 播放器程序——然后才能播放视频。
很多人都喜欢 Loki 夹克,但是看一下 Loki 美国官网——他们的首页视频寄存在 Vimeo 服务器:
仔细看就能发现 JS 播放器在 DOM 完成之后立马请求了,但是很久之后才完全加载并开始播放视频。
2.无法右键保存视频
大多数长篇视频内容都是基于JS 播放器进行分发的——影像日志、电视、电影。通常这些播放器为用户提供了很方便的分享链接和书签工具,这样用户就可以随时返回 YouTube 或者其他任何地方并再次找到这个视频。作为对比,微视频内容——比如表情包和图片动画——通常不是经由播放器传递的,用户希望能够下载 GIF 并分发给朋友,就像他们对网络上其他图片进行的操作一样,比如“这只跳舞的猫咪太可爱了,我必须分享给所有的好友!”
如果你用的是 <video>
标签,那用户就不能右键另存为了。而前面提到的跳舞的猫咪也就是出色却让人失望了。
3.自动播放被滥用
最后,采用 <video>
标签和 MP4 而不是 <img>
标签和 GIF,就会发生在你浏览网页时突然蹦出猫鼠游戏的视频,这就是为了吸引用户注意而滥用 <video autoplay>
自动播放属性的案例。移动浏览器都忽略或者禁用了自动播放属性,需要全屏才会启动这一属性。在过去几年中,苹果和谷歌都放松了这方面的限制,允许 <video>
下类似 GIF 的内容自动播放。
然而,广告厂商又在滥用这一特性,引发了更加严格的限制:如果要用自动播放 <video>
的功能,必须将内容属性设置为静音 <muted>
或者完全去除声轨。
但是我们已经有了动态 WebP 和动态 PNG 啊!
GIF 并不是唯一的具有动画小姑的图片格式。WebP 和 PNG 也都支持动画功能。但是,与 GIF 类似的是,他们都不是为动画设计的。这就导致,和专注于视频编码的格式如 H.264、H.265、VP9 和 AV1 相比,他们的动画文件体积巨大。
动态 PNG 在各种浏览器中得到了广泛支持,但是他也有 GIF 存在的色彩问题,同时依旧不能高效压缩视频。
动态 WebP 能好一些,但是与真正的视频格式相比,还是有很多问题。除了不具备标准格式外,动态 WebP 缺少色度子采样和宽域支持。更进一步地,动态 WebP 的支持生态十分破碎,并非所有的 Android、Chrome 和 Opera 都支持动态 WebP,甚至浏览器都把支持 WebP 作为一项特性来打广告:Chrome 42、Opera 15+ 和 Android 5+ 才行。
所以尽管动态 WebP 比 动态 GIF 和 PNG 的压缩效率要高,但我们还可以做得更好(见上面的图片压缩对比)。
自己测试一把
Having our cake and eating it too
Safari Technology Preview 为 <img>
标签增加了真正的视频功能(如 MP4),修复长期存在的性能和 UX 问题。现在,我们的微视频可以做到体积小、效率高(正如通过 <video>
标签传输的 MP4 文件那样),并且可以轻松地预加载、自动播放和分享(就像分享 GIF 那样)。
<img src="ottawa-river.mp4">
那么具体说来,如此操作究竟能有多么快速?打开浏览器的开发者工具,将 Safari Technology Preview 和其他浏览器对比一下吧:
很不幸的是, Safari 在 WebPageTest 上表现欠佳,而要打造可信的 benchmark 也非朝夕之功。与之类似的,Tech Preview 的使用量还很低,所以基于 RUM 工具进行性能比对也还不太实际。
然而,我们在两方面可以有所作为。一是对比最初文件的体积,二是利用 Image.decode() 命令,测试不同资源对于设备的影响。
节省流量
首先要说明的是,图片体积就表征了流量节省情况。为了进行对比,实验者将 giphy.com 前 100 的动图 GIF 分别转换为 vp8/vp9/webp/h264/h265 格式.
声明:这些结果仅可作为方向示意。每个解码器都可以做更多调试,正如可以看到的, vp9 要比默认的 vp8 输出差很多。还需要进行更详细的、把 SSIM 考虑在内的测试。
以下是此次测试的中位数 (p50) 结果:
格式 | 尺寸中位数 | 变化百分比中位数 |
---|---|---|
GIF | 1,713KB | |
WebP | 310KB | -81% |
WebM/VP8 | 57KB | -97% |
WebM/VP9 | 66KB | -96% |
WebM/AV1 | TBD | |
MP4/H.264 | 102KB | -93% |
MP4/H.265 | 43KB | -97% |
WebP 的体积比 GIF 要小,但是其他任何形式都比 WebP 还要小。这没什么奇怪的,因为这些新的视频编码就是为在线视频流高度优化的。H.265 正如预期的 AV1 一样表现不错。
这样的优势不仅是传输更快,而且是为终端用户大量节省费用。
总而言之,在 <img>
标签中使用视频在移动网络情况下速度更快。
解码和视觉效果的提升
下面要考虑的是解码和浏览器端的播放效果。H.264 和 H.265 的优势是硬件解码。
如何进行测量?
因为浏览器尚未完善首屏图片 API,可以采用 Steve Souder 的 User Timing and Custom Metric strategy 作为近似手段,评估图片合适向用户展示。他并不测量帧频,但是却大致告诉人们第一帧何时播放。更好的是,可以使用新采纳的 Image.decode() 测量解码性能。在下方的测试页面中,实验者在 <img>
标签中注入了一张特定的 GIF 和 MP4 100 次,并对比了解码和绘图性能。
let image = new Image;
t_startReq = new Date().getTime();
document.getElementById("testimg").appendChild(image);
image.onload = timeOnLoad;
image.src = src;
return image.decode().then(() => { resolve(image); });
结果令人印象深刻。在实验者所用的 2017 MacBook Pro 进行不联网本地测试的情况下,GIF 比 MP4 要多花 20 倍的时间才能绘制出第一帧(由 onload 事件信号而来),前者的解码时间是后者的 7 倍。
很好奇嘛?复制这个项目,自己测试下吧。需要注意的是,联网会对这个 GIF vs MP4 的试验结果造成扭曲。尤其是,在最后一个比特结束之前,解码就已经开始了,这样在传输、播放和解码之前的差距就变得小多了。这表明,节省流量可以大幅度提高用户体验。然而,仅在本地机器进行测试,你就能够发现,视频在节能方面也有着极佳的表现。
如何改进?
既然 Safari Technology Preview 已经支持这样的设计模式了,人们该如何利用这一功能,而不必在不支持的浏览器上显示破碎的图片?好消息是这个其实很简单。
方法 1 :使用响应式图片
理想情况下,最简单的是利用 HTML5 中 <picture>
标签的 <source type>
特性:
<picture>
<source type="video/mp4" srcset="cats.mp4">
<source type="image/webp" srcset="cats.webp">
<img src="cats.gif">
</picture>
其实事情就到此为止了。然而,Safari 中有一个 WebKit bug 导致预加载器会下载第一个 <source>
,而无关乎类型声明。主 DOM 加载器能够发现这一错误并选择正确的加载,但是这一损害是不可逆的:预加载器充分利用了机会将图片下载下来,而更重要的是这浪费了流量。好消息是,有人上报了这一 Bug,应该会在 Safari TP 45 中得到修复。
简言之,在 Safari 下一版本达到 90% 的用户基数之前,不推荐使用 <picture>
和 <source type>
。
方法2:使用 MP4、动画 WebP 和 GIF 回调
如果你不想改变 HTML 标记,你也可以使用 HTTP 将 MP4 附带内容协议发送给 Safari。要这么做,你需要基于 Accept
和 User-Agent
的 Header 生成动态图片(跟从前一样)和 Vary
回复的多个拷贝。
在 WebKit BUG 179178 发布后,这一切简单多了。你可以使用 Accept: video/*
进行测试(就像之前使用 Accept: image/webp
进行测试一样)。但是不同的浏览器有着不同的支持类型,具体如下
浏览器 | 接受的 Header | 响应 |
---|---|---|
Safari TP41+ | H.264 MP4 | |
Accept: video/mp4 | H.264 MP4 | |
Chrome 42+ | Accept: image/webp | aWebP |
Opera 15 | Accept: image/webp | aWebP |
Accept: image/apng | aPNG | |
Default | aGif |
在 Nginx 中,将与下述类似:
map $http_user_agent $mp4_suffix {
default "";
"~*Safari/605" ".mp4";
}
location ~* .(gif)$ {
add_header Vary Accept;
try_files $uri$mp4_suffix $uri =404;
}
当然了,不要忘记利用 Vary: Accept, User-Agent
告诉他们分别缓存每项响应。实际上,你应该将 Cache-Control 设置为私密属性,并利用 TLS 来保证不太熟练的 ISP 性能提高代理不会缓存内容:
GET /example.gif HTTP/1.1
Accept: image/png; video/*; */*
User-Agent: User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/605.1.13 (KHTML, like Gecko) Version/11.1 Safari/605.1.13
…
HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Length: 22378567
Vary: Accept, User-Agent
方法3:使用 RESS 和回调 <video>
标签
如果你可以自由操纵 HTML,你可以采用 RESS 技术(Responsive-Server-Side)。这样可以将浏览器的检测逻辑移动到 HTML 的输出中。
例如,对于 PHP 你可以这么做:
<?php if(strlen(strstr($_SERVER['HTTP_USER_AGENT'],"Safari/605")) <= 0 ){ // if not firefox ?>
<img src="example.mp4">
<?php } else {?>
<img src="example.gif">
<?php }?>
如上文所示,一定记得将 Vary: User-Agent
的响应提供给 CDN 供应商,告诉他们,你的 HTML 有不同版本的缓存内容。有些 CDN 厂商自动支持 Vary 抬头,而其他厂商需要经过简单的 CDN 配置才可以。
额外奖励:不要忘记删除音轨
现在既然你决定不将 GIF 转为 MP4 而是将 MP4 转为 GIF,需要记住的是,删除音轨以节省体积。既然已经知道这些动画都将是静音播放的,所以把声音和其他不必要的都删除就好了。对于 ffmpeg 最简单的方式是:
ffmpeg -i cats.mp4 -vcodec copy -an cats.mp4
是否有体积限制?
在撰写本文期间,Safari 会无区分地下载所有 <img>
标签中的视频,无论体积大小如何。一方面,这会帮助提高浏览器性能,所以是需要的;另一方面,如果向用户推送了一条 120 分钟的视频,这就很可怕了。实验者测试了不同体积的视频,发现只要用户在线,所有的视频都会下载下来。所以,要对用户友善些:如果想推送长视频,从性能考虑还是用 <video>
标签吧。
下一步是什么?交互式视频和背景视频
既然已经可以通过 <img>
标签传送视频, 那就会有很多新的用例了,比如交互式视频和背景视频。
比如,可以将 MP4 放在 srcset
中,利用 Client Hints 和 Content-DPR 给出不同的反应类型,并利用 <picture media>
添加艺术效果——这里有无限可能。
<img src="cat.mp4" alt="cat"
srcset="cat-160.mp4 160w, cat-320.mp4 320w, cat-640.mp4 640w, cat-1280.mp4 1280w"
sizes="(max-width: 480px) 100vw, (max-width: 900px) 33vw, 254px">
CSS 中的视频 background-image: url(.mp4)
同样也会奏效。
<div style="width:800px, height: 200px, background-image:url(colin.mp4)"/>
结论
Safari Technology Preview 为 <img>
标签引入视频功能,能够实现与 GIF 类似的良好体验,同时可以避免 GIF 文件的性能和质量损耗。对于用户、开发者、设计师和网络人员来说,这一功能真是非常赞。这一升级除了带来巨大的性能提升之外,还打开了媒体和电商企业数年来长期渴求的很多新的用例。希望其他浏览器能够马上跟进:谷歌、微软、火狐、三星……该你们接招了。
阅读英文原文:Evolution of < img >: Gif without the GIF
本文文字及图片出自 InfoQ
你也许感兴趣的:
- HTML行为属于HTML
- 趣图:HTML and CSS
- 即使不编程,你也应该学会HTML和CSS的9大理由
- 【外评】电脑从哪里获取时间?
- 【外评】为什么 Stack Overflow 正在消失?
- Android 全力押注 Rust,Linux 却在原地踏步?谷歌:用 Rust 重写固件太简单了!
- 【外评】哪些开源项目被广泛使用,但仅由少数人维护?
- 【外评】好的重构与不好的重构
- C 语言老将从中作梗,Rust for Linux 项目内讧升级!核心维护者愤然离职:不受尊重、热情被消耗光
- 【外评】代码审查反模式
你对本文的反应是: