一个动图需求引发的思考

需求

移动端页面,原本是一个大背景图,现在有一部分需要做成动画,而这个动画大概比较复杂,所以由设计来做成 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 实现那样的动画需要多大的成本,性能又如何呢?

相关推荐
桂月二二23 分钟前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
hunter2062062 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb2 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角2 小时前
CSS 颜色
前端·css
浪浪山小白兔3 小时前
HTML5 新表单属性详解
前端·html·html5
lee5763 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579653 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter
limit for me4 小时前
react上增加错误边界 当存在错误时 不会显示白屏
前端·react.js·前端框架
浏览器爱好者4 小时前
如何构建一个简单的React应用?
前端·react.js·前端框架
qq_392794484 小时前
前端缓存策略:强缓存与协商缓存深度剖析
前端·缓存