背景
在开发小程序的时候,用户体验是一个重要的关注点。糟糕的用户体验会让用户使用小程序的意愿大大降低,甚至不再使用。优化用户体验是一门复杂的学问,涉及到产品交互设计、程序性能等等各方面的工作,很难一蹴而就,只能在各个场景下,对于不同的关注点,分别去采取对应措施进行优化,并且还要辅以对应的设计规范、防劣化措施等等,使得体验能够始终维持在设立的基线之上。
今天想要分享的体验优化以项目中的图片为切入点,针对图片加载场景,优化用户在使用小程序时浏览图片相关的体验。
如何优化
图片浏览体验的主要优化目标有两点:
- 让图片加载更快,能够快速为用户提供内容,提升加速度,进而提高转化率;
- 在加载过程中给用户明确的反馈和预期,如加载时的动画、加载失败的提示;
给用户带来焦虑的体验
达到以上目标,我们可以从图片资源本身以及图片加载过程这两个角度去考虑优化措施。
图片资源优化
图片资源优化的目的是减小传输内容的大小,从而减少图片下载耗时,进而加快图片展示。
改变图片资源的体积有三大方法,主要涉及压缩质量、图片资源的格式、图片分辨率。我使用了火山引擎的 veImageX 来处理图片,经过一定配置之后,只需要修改 URL 里的参数,就可以动态修改图片的格式、质量等等参数,很方便,搜索引擎,搜索关键词“火山引擎 ImageX”,可以找到他们的体验版本,不用注册就能用。
合适的图片质量
我们给用户提供的图片质量,应该与各种场景挂钩。比如,当我们检测到用户处于弱网环境时、亦或者当前图片场景(比如列表预览)并不需要高质量质量图片时,可以给用户提供小体积而质量稍差的图片,降低图片传输的成本;当用户处于wifi环境、或者是需要预览高清大图时,提供高质量的图片以强化用户的看图体验。
具体的图片加载质量应该有着各种各样的场景化策略。
veImageX 支持在 URL 中传入质量参数值来控制下发的图片质量,转换了一张 lena 的 jpeg 格式图片来对比下效果。
压缩质量 | 25 | 50 |
75 | 85 | 95 | 100 |
---|---|---|---|---|---|---|
图片 | ||||||
体积 | 35KB | 49KB | 77KB | 116KB | 250KB | 583KB |
这几张图片分别使用了 25、50、75、85、95、100 的质量参数,体积也是随着质量参数的增大而膨胀。我们可以看到 25、50、75 这几张图片的质量差异还是相当明显的,高质量图片中的色块和噪点一样的东西明显减少了,而质量 75 以上的几张图片,说实话肉眼看区别不太大,但是体积却膨胀了不少。所以一般情况下应该选择 80 左右的质量参数就差不多了。
更优的图片格式
我放了张常见的 720*862 的图片转换了一下格式,下载下来对比文件大小。可以看到,现代图片格式(webp、avif、heic)的图片体积明显比 png、jpeg 这些格式要小不少。而且图片质量上,肉眼基本看不出差异。
压缩质量75 |
png(有损) | jpeg | webp | avif | heic |
---|---|---|---|---|---|
图片 | |||||
信息 | |||||
体积 | 219KB | 102KB | 69KB | 32KB | 26KB |
与png相比 | - | -53% | -68% | -85% | -88% |
处理的图片,分辨率不变的前提下最多比 png 格式图片减少了 88% 的大小(heic 格式),并且肉眼看不出质量的明显差异。
如果能够用上这些高压缩率的图片格式,肯定能够节省图片下载的网络耗时。
编解码性能
除了关注图片的体积之外也仍然需要关注不同图片格式在终端的性能,这里编码和解码都用开发机进行测试了,我们找了几千张图片在服务端的测试结果,测试图片以分辨率 400x300、1200x700 为界,在三个区间内取相同图片样本量。
测试环境:开发机,8C16G,Intel(R) Xeon(R) Platinum 8260 CPU @ 2.40GHz
图片格式 | 质量参数 | 压缩比例 | 解码耗时ms | 编码耗时ms | PSNR |
---|---|---|---|---|---|
jpeg | 30 | 30% | 11.0 | 15.1 | 38 |
50 | 40% | 12.0 | 15.0 | 40 | |
75 | 60% | 12.0 | 16.2 | 46 | |
90 | 100% | 13.5 | 16.7 | 51 | |
webp | 30 | 20% | 16.9 | 170.1 | 39 |
50 | 28% | 19.3 | 179.2 | 41 | |
75 | 38% | 21.5 | 190.2 | 42 | |
90 | 76% | 29.4 | 221.9 | 47 | |
HEIC/BVC1 | 30 | 10% | 58.9 | 13.9 | 38 |
50 | 10% | 60.8 | 13.9 | 38 | |
75 | 22% | 70.9 | 13.8 | 42 | |
90 | 43% | 78.2 | 14.3 | 45 | |
AVIF | 30 | 7% | 60.6 | 1224.9 | 36 |
50 | 18% | 65.4 | 1413.5 | 40 | |
75 | 24% | 67.3 | 1497.8 | 42 | |
90 | 44% | 76.4 | 1382.3 | 46 |
所以从表格上来看,在解码性能上选择:jpeg
自适应的图片分辨率
一张分辨率为 3648*2736 的壁纸,体积为 8M
除了格式之外,图片分辨率也是一个可以优化的点。如果我们想要在应用中展示上面这张图片,在大多数情况下 3648 × 2736 的分辨率都是过剩的。我们可以根据不同的场景,去对图片进行缩放,以提供合适的分辨率。在缩放后,可以进一步减小图片的体积。
webp | avif | heic | |
---|---|---|---|
原大小 | 1.9M | 1.1M | 999K |
等比例缩放到1920宽度 | 856K | 733K | 705K |
加载过程优化
除了被加载的图片资源本身进行优化之外,保障用户体验的另一个点在于优化加载过程。
图片懒加载
图片懒加载指的是在图片进入可视区域之前不进行图片的实际加载,这样能够保证首先加载首屏图片,减少并发的图片请求。在 Web 端,我们通常使用 IntersectionObserver
来实现对应的功能,目前在小程序上,不管是抖音小程序还是微信小程序,其提供的 image 组件(抖音和微信 WebView 模式)都支持了 lazy-load
属性,加载边界为上下左右三屏范围,即仅当页面滑动到图片上下三屏幕时才开始发出网络请求加载图片。
对于大量展示图片的列表场景、照片墙场景等,启用懒加载应当对页面加载性能有明显的提升。
以 Web 为例,启用懒加载的场景下,首屏只加载了 5 张图片,只有向下滚动到一定范围内才会发起后续图片请求
加载动画与反馈
使用原生 image 组件,不进行任何额外处理的情况下,不管是在图片加载过程中,亦或是图片 URL 有误、下载失败的情况下,图片区域都是一片空白。
加载过程慢 | URL 不可访问时,加载失败没有任何反馈 |
尼尔森提出的[十条可用性原则]的第一条就是 Visibility Of System Status(系统状态的可见性),即让用户知道当前在发生什么,应用的状态如何需要及时给用户反馈。因此,在以上两种状态中(加载中、加载失败),我们都期望能够表现得足够明显,给予用户一定的反馈。
在弱网情况下,图片下载的耗时可能会被拉长。此时,无论是骨架屏还是一段loading动画,都可以有效缓解用户的焦虑;
loading 效果
而在图片加载失败之后,一张占位图加上一段加载出错的提示,无疑远远胜过一片空白。
Chrome 系浏览器默认的效果
尽管不好看,但有比没有好
以 B 站的个人空间页面举例,对于不同的含义的图片,其使用了不同的占位图片,可以获得更好的效果。需要结合具体场景来选择合适的占位提示图。
结合场景使用不同兜底占位图
动手改造
实际的项目改造也打算按照上面提到的这两个角度来进行。首先就打算把目前应用的最多的图片格式 jpeg 都换成高压缩率的格式。根据抖音小程序开发者文档中属性描述,应该能够支持 webp 图片的加载。
抖音小程序 image 说明
实际上经过测试,直接使用 image 组件加载多种格式的图片。安卓上抖音小程序里 heif、avif、webp 这几种格式都能够加载,而 iOS 上却只能够加载 avif、webp 的图片。(iOS 不支持加载 avif 动图)
Android | iOS(avif 只支持静图) |
根据上面的测试结论,在安卓上可以使用 heif 格式图片,而 iOS 在静图场景可以使用 avif 格式图片,而动图场景下,只能退而求其次,使用 webp。
如此一来,对于不同的系统、平台,要达成选用最优的格式的目的,我们需要分别进行测试。而且对于不同版本的抖音宿主,我们也需要去回归其对于不同图片格式的兼容性,开发测试成本明显提高了。
图片加载
在搜索解决方案的时候,发现veImageX这边提供了一个抖音小程序的 sdk ,号称通过“格式探测”的手段解决了这个问题,能够在不同的环境下,选择最合适的图片格式进行加载。
我按照文档指引,实际写了个demo接入尝试了下格式自适应能力。
按照文档说明,formats 配置是逐级 fallback 的,那安卓机型支持 heif 的情况下应当加载 heif 图,iOS 则应该加载 webp 图片。使用真机测试,确实符合预期。安卓下加载了 heif 图,iOS 加载了 webp 图片。
Android 加载 heif
iOS 加载 webp
图片后缀 ~tplv-2ulrpbhhis-resize:480:q75.heif,分别对应了缩放的尺寸、图片压缩质量和图片转码目标格式,也就是说实际加载的图片已经经过了服务端的处理,缩放为了480px 宽度、质量 75 的对应格式图片,这张 heif 图片的体积减小到了 16 KB,比起处理前的超大图,节省了巨多体积,加载当然也快了很多。
此外,根据文档介绍,这个 sdk 中也提供了诸如懒加载、占位图还有图片监控等的能力。
默认加载动画 | 默认加载错误占位图(自定义文字) |
如果默认的加载时的动画效果以及错误占位图不能满足需要,也可以通过 placeholder 和 errorDataUrl 属性配置其它图片。我试了下,这两个属性都可以配置任意有效的图片地址,但是对于这两个场景(极有可能弱网/无网),我们应当配置不需要网络也能成功加载的图片,即使用 base64 data url 或者 svg data url 等等。这样才能起到应有的效果。
图片监控
文章开头提到,优化用户体验除了优化措施本身,防劣化也是一个重要的组成部分。防劣化的前提就是需要能够设定一个基线,而设定基线则要求我们能够将当前的用户体验用合适的指标量化出来。这个组件内部就提供了图片加载质量上报的能力,能够上报图片加载网络各阶段(DNS查找、http建连、下载等等)的耗时、图片加载是否成功、图片尺寸等等。
接入方式也比较简单,只需要在项目入口的地方,前置调用个函数初始化一下,然后在小程序后台把对应的数据上报域名加到白名单里就可以。
App({
onLaunch: function () {
console.log('App Launched');
// ...各种逻辑
initLoggerInstance(logger) // <-- 只要初始化一下就可以,具体配置可以看看文档
}
});
接入了图片监控之后,就能在 veImageX 的控制台上去查看相关的指标,还有一些聚合指标,比如总加载次数、错误率之类的,还可以设置一些监控报警策略。
小结
事实上,这个图片组件的功能基本上能够满足图片优化的诉求了,配置自由度也比较高,能够定制出各种不同的样式和效果,适配不同的场景。
by the way,veImageX不仅提供小程序图片优化SDK,也提供包含素材托管、资源分发、图像智能处理的一整套解决方案。而且他们最近有个流量包促销活动(可戳链接了解),说是小程序开发者可以享受 6 折的流量包优惠,但是我实际购买时发现其实没有限制,只要按照文档指引去注册账号、开通服务时填个邀请码就能买到优惠流量包了,这么香的价格,大家赶紧去薅羊毛吧~