2022年度数据账单项目总结
有两三年没做过年度账单 H5 项目了,接到需求,要在 2022 年末上线,当即火急火燎进行技术调研。看了往年的年度账单,就是一张背景图加上文字渐入效果,相当简单。2022 年时间上还充裕,花了一周时间去了解平时不怎么用的 GSAP 和 animeJS,参考了网易云音乐的年度账单效果,发现需要用 AE 来进行场景的设计,可是自己和 UI 设计师也不熟悉 AE,无奈最后决定使用简单的方法来实现。
交互框架搭建
根据研发讨论,我们的交互应该是整屏的内容切换,切换方式就是上下滑动或者左右滑动,页面进场、离场或者用户操作的过程中页面中的元素应该有对应的动画效果。基于这些想法,想起了很久没用的 fullPage.js
,貌似这个满足当前的需求,然而查看证书发现要收费才能使用了,由于平常使用场景比较少就不考虑付费使用,只能另找其它开源库。后来想了下,经常用的 swiperJs
不是也可以满足需求么,滑动就是这个库的强项,而且还开源,所以就选它了。
项目使用了 Vue3.x 版本,而 SwiperJS 也默认支持了 Vue3,所以使用时直接安装引入即可。
安装 swiper:
pnpm i swiper
使用
<swiper ref="swiperRef"
:effect="'fade'"
:observer="true"
:slides-per-view="1"
:spaceBetween="0"
:direction="'vertical'"
:parallax="true"
:modules="modules"
@init="onInit"
@transition-end="onTransitionEnd">
<swiper-slide class="slide-1"></swiper-slide>
<swiper-slide class="slide-2"></swiper-slide>
<swiper-slide class="slide-3"></swiper-slide>
</swiper>
import { watch, ref, inject } from 'vue'
import { Swiper, SwiperSlide } from 'swiper/vue'
import type { Swiper as SwiperType } from 'swiper'
import { Parallax, EffectFade } from 'swiper'
import 'swiper/css'
import 'swiper/css/effect-fade'
const swiperInstance = ref()
const modules = [Parallax, EffectFade]
const current = ref(0)
/**
* 滑块初始化
* @param swiper
*/
const onInit = (swiper: SwiperType) => {
swiperInstance.value = swiper
swiper.slideTo(current.value)
}
/**
* 记录滑块索引
* @param swiper
*/
const onTransitionEnd = (swiper: SwiperType) => (current.value = swiper.activeIndex)
.swiper {
width: 100%;
height: 100%;
}
.swiper-slide {
height: 100%;
width: 100%;
font-size: 18px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
.swiper-slide-content {
width: 100%;
height: 100%;
}
动画实现
要实现动画效果有很多选择,为了缩小项目体积,摒弃了 anime.js
和 GSAP
等相关动画库,选择单纯使用 CSS3 的 animation 来实现动画效果。动画库的话就使用 animate.css
,再加上 SwiperJS
自带的视觉差效果和渐入渐出转场,也基本满足需求了。
Animate.css 使用
SwiperJS
中直接使用 animate.css
会存在一个问题,就是不会重复执行动画。要解决这个问题其实只需要让动画元素重新渲染即可,要重新渲染最直接的方法是通过 v-if
来控制 swiper-slide
的显示隐藏。如果通过监测 slide-change
事件来改变当前滑块的索引,这样做会出现滑块转场动画执行不完全的状态(比如 fade 转场透明度没执行完),解决这个问题直接通过监测 transition-end
事件进行当前滑块索引更新即可。
替换掉 Anime.js
前期考虑到如果时间充足的话可以使用 anime.js
来细化一些动画的执行,不过后来项目写到尾声才发现,时间不够充裕。前期用到 anime.js
的地方主要是图片资源加载百分比数字动画和文字逐行渐进动画,而在使用的过程中发现 anime.js
本身存在一些问题,加上考虑文件体积,故使用其它方法将其换掉。
百分比数字动画
如果直接修改百分比数字到话,因为网络延迟方面的原因,百分比可能会一下子由一个很小的数字直接变到很大的数字,这样用户看着就会觉得很突兀。
通过 setTimeout
实现
import { ref } from 'vue'
const progressRef = ref(0)
/**
* 更新进度条百分比
*/
let temp = 0
let timer: any = null
const updateProgress = (num: number) => {
window.clearTimeout(timer)
if (temp >= num) return
temp++
progressRef.value = temp
timer = setTimeout(() => updateProgress(num), 16)
}
由于浏览器显示频率的问题,通过 setTimeout
来进行刷新百分比可能出现丢帧现象,我们可以通过 requestAnimation
来优化,具体可以看我的另一篇文章。
一些优化
优化主要围绕代码体积、页面加载、渲染效率等方面进行。
可视化查看代码体积
要减少代码体积,我们首先要知道哪些地方代码体积太大了。可视化分析代码体积的库很多,vite 中可使用 rollup-plugin-visualizer
。
pnpm i rollup-plugin-visualizer -D
安装完后,需要在 vite.config.ts
打包配置中引入。
import vue from '@vitejs/plugin-vue'
import { visualizer } from 'rollup-plugin-visualizer'
export default () => {
return defineConfig({
plugins:[vue(), visualizer()]
})
}
然后执行构建
pnpm run build
构建后在项目根目录会生成一个 stats.html
的文件,直接浏览器打开就可以看到各个代码占的体积。
当然,没必要将分析文件上传到远程代码仓库,可在 .gitignore
中增加排除。
移除 vue-router
经过代码可视化分析,发现项目模板中引入了路由,但由于年度账单无需多页面跳转,没必要引入 vue-router
,移除后能减少几十 K 的代码体积。
去除 animate.css
多余类
项目中使用到的动画有限,没必要引入 animate.css
所有的动画文件。
要按需引入我们需要将 animate.css
的代码仓库拉取下来,在本地修改 animate.css/source/animate.css
中的引入文件,然后执行重新构建。
pnpm run start
在需要使用的地方引入新构建的 animate.css/animate.min.css
文件就行。
遇到的问题
1. 分享出去是链接,不是卡片
微信开放全域名访问后出现的限制。目前可从 公众号菜单进入分享
、扫描二维码后分享
、添加到收藏然后从微信我的收藏进入分享
是正常的,其他地方进入分享都是链接。
2. 如何真机调试
由于一些效果在浏览器上复现不了,需要真机上调试。真机调试一般有两种方法,一种是安装 spy-debugger ,另一种是使用 Chrome 浏览器自带的远程调试 chrome://inspect
。 两者各有利弊,前者需要安装证书,后者需要通过 USB 连接调试。具体可参考我的另一篇文章。
3. 上线前图片存到 OSS
图片务必存储到 OSS,不然普通服务器带宽承受不住,一上线访问数一多服务器就会奔溃。
4. 打包后样式不一致
这主要是使用了 autoprefixer
自动加浏览器前缀造成。解决方案是使用规范的样式,比如使用 Photoshop 复制的渐变背景 CSS 是自动添加了浏览器前缀,此时解析的时候就会出现异常,需要手动删掉无用的浏览器前缀并只保留规范的写法。