一个动图需求引发的思考

需求

移动端页面,原本是一个大背景图,现在有一部分需要做成动画,而这个动画大概比较复杂,所以由设计来做成 GIF。于是重新切图,将那部分不再保留在背景图上,用 GIF 来展示。但问题是 GIF 太大,于是产品问能不能在弱网时先展示静态图。

vue img @load v-if

于是我就在 GIF 完成加载前展示 jpg 图片,因为是在 Vue 中,所有直接使用 img 的 load 事件就可以了,但是没起作用:

js 复制代码
<img
  v-if="isLoad"
  src="path-to-your-gif.gif"
  alt="动图"
  @load="isLoad = true"
/>
<img
  v-else
  src="path-to-your-jpg.jpg"
  alt="静态图片"
/>

new Image()

利用 new Image() 创建一个新的 image 对象,并开始加载 GIF,当加载完成(onload 事件触发)时,将展示的图片 src 更新为 GIF 的 url,重新渲染 img 元素。

js 复制代码
<img
  :src="imgSrc"
  alt=""
/>

const imgSrc = ref("");
const img = new Image();
img.src = "path-to-your-jpg.jpg";
img.onload = () => {
  imgSrc.value = "path-to-your-gif.gif";
};

Vue img @load v-show

后来才发现,第一种方式不生效的原因是使用了 v-if,当使用 v-if 时,页面渲染就没有 v-else,所以 GIF 加载完成时,也无法展示。因为我一开始背景图上还有图片,所以没有发现。

js 复制代码
<img
  v-show="isLoad"
  src="path-to-your-gif.gif"
  alt="动图"
  @load="isLoad = true"
/>
<img
  v-show="!isLoad"
  src="path-to-your-jpg.jpg"
  alt="静态图片"
/>

background

利用背景图,也可以做占位图。但是它的问题是,在加载的过程中,图片从上到下逐渐的覆盖背景图,有点奇怪。不过用背景图也有好处,那就是在图片加载失败时,仍然能展示静态图。

css 复制代码
.img {
  background-image: url("path-to-your-jpg.jpg");
  background-size: 100%;
  background-position: center;
  background-repeat: no-repeat;
}

GIF

后来发现,其实 GIF 图是逐帧加载的,所以默认一开始就会显示静态图,并不需要再用一张静态图占位。我搜了一下,可能大部分浏览器是支持这样的。

也就是说,完全不做任何处理。

js 复制代码
<img
  src="path-to-your-gif.gif"
  alt="动图"
/>

但是这种方式有它的缺点,和以上占位图的方式比较:

  • 占位图:在完全加载完成之前,显示 jpg 图片,但是用户可能不知道这是个动图。而且 jpg 图片和 GIF 替换时会有一闪的感觉。
  • 直接 GIF:逐帧加载,在完全加载完成之前,会显示一个比较卡顿的过程。

考虑到如果大多数情况下,用户网速 OK,那似乎直接 GIF 会更好一点。

第三方库

简单搜了下,vue-load-image 短小精悍、方便易用。它的功能很简单,就是在图片加载完成之前显示 loader,加载失败显示失败信息。也大概看了下源码,其本质也是使用 new Image 加载图片,onload 事件、onerror 事件时分别改变状态 status:

js 复制代码
<template>
  <div class="vue-load-image">
    <slot v-if="status === 'loaded'" name="image" />
    <slot v-else-if="status === 'failed'" name="error" />
    <slot v-else-if="status === 'loading'" name="preloader" />
  </div>
</template>

createLoader() {
  this.destroyLoader()
  this.img = new Image()
  this.img.onload = this.handleLoad
  this.img.onerror = this.handleError
  this.img.crossOrigin = this.crossOrigin
  this.img.src = this.src
},
handleLoad() {
  this.destroyLoader()
  this.status = Status.LOADED
  this.$emit('onLoad')
},
handleError(error) {
  this.destroyLoader()
  this.status = Status.FAILED
  this.$emit('onError', error)
},

还有更多第三方库,比如:vue-lazyload,功能更多一些。

总结

前端就是关注用户体验,前端就是关注各种细节,实际工作中,每一个小小的问题,如果深入探索,就能发现更多本质的东西。对于初学者来说,不要放过任何一个你不能理解的问题,对于职场人来说,被迫去做去思考你不感兴趣、你觉得不重要的事情,也许很快你就会有新的收获。

不过对于 GIF 图为什么那么大,我还是不理解,也没有压缩的空间了,这个也需要深入的学习制作一下。

还有是不是并不需要 GIF,用 CSS 可以实现呢?用 CSS 或 js 实现那样的动画需要多大的成本,性能又如何呢?

相关推荐
dog shit17 分钟前
web第十次课后作业--Mybatis的增删改查
android·前端·mybatis
我有一只臭臭17 分钟前
el-tabs 切换时数据不更新的问题
前端·vue.js
七灵微21 分钟前
【前端】工具链一本通
前端
Nueuis1 小时前
微信小程序前端面经
前端·微信小程序·小程序
_r0bin_4 小时前
前端面试准备-7
开发语言·前端·javascript·fetch·跨域·class
IT瘾君4 小时前
JavaWeb:前端工程化-Vue
前端·javascript·vue.js
potender4 小时前
前端框架Vue
前端·vue.js·前端框架
站在风口的猪11084 小时前
《前端面试题:CSS预处理器(Sass、Less等)》
前端·css·html·less·css3·sass·html5
程序员的世界你不懂5 小时前
(9)-Fiddler抓包-Fiddler如何设置捕获Https会话
前端·https·fiddler
MoFe15 小时前
【.net core】天地图坐标转换为高德地图坐标(WGS84 坐标转 GCJ02 坐标)
java·前端·.netcore