1. 背景
最近开发了一个录取通知书流动,获得了比拟好的成果,所以写篇文章总结一下教训,有趣味的能够点击下方链接,请在挪动端关上。
录取通知书流动
2. 目录
- 动画成果实现计划比照
- 挪动端适配
- 视频预加载
- 学习材料举荐
3. 动画成果实现计划比照
3.1 程度静止动画
纯 css 实现计划
大略的代码
.animate .p3_d {animation: p3D 1s ease-in 0.1s forwards;}
@keyframes p3D {
0% {transform: translate3d(24rem, 0, 0);
}
90% {transform: translate3d(7.2rem, 0, 0);
}
100% {transform: translate3d(8.2rem, 0, 0);
}
}
实现成果地址
能够看到整体的动画成果其实不够灵便,显得板滞,影响动画成果的两个关键因素一个是关键帧(keyframes),一个是动画的过渡成果(transition-timing-function),这里应用的是 ease-in,个别好的过渡成果都会用贝塞尔曲线实现(感觉都能够独自写一篇文章解说贝塞尔曲线对动画的影响了,篇幅起因不做开展)。
集体倡议如果设计师可能在这两个关键因素上提供帮忙,那么就用 css 实现,如果不能,能够思考其余的计划
应用 dynamics.js 实现
dynamics.js 的官网,借助这个库能够实现一些真切的物理静止动画
const p30 = document.querySelector('.p3_0');
dynamics.animate(p30, { translateX: '1.7rem'}, {
type: dynamics.spring,
frequency: 40,
friction: 200,
duration: 1000,
delay: 0.2
});
能够看到它的应用形式相当简略,要说毛病,个人感觉就是源码用 coffee.js 写的,这门语言当初根本曾经被 ts 代替了,如果感觉它不能满足需要,想改源码可能比拟艰难,上面看一下应用 dynamics.js 实现的成果
实现成果地址
其实动画成果就是这样,乍一看实现成果都差不多,但认真看,它们之间还是会有很多轻微的差异,往往就是这些渺小的差异,值得咱们深入研究,让动画成果显得更加灵动,真切。
3.2 随机静止动画
形式 1:纯 css 实现相似的静止(不是随机)
能够参考这个例子,这个例子尽管不是真正的随机静止,然而实现成果也不错,所以做为一个参考的案例也放进来一起比拟。
察看 optionFloatAniP2Key 的实现,能够看到,为了静止成果的平滑,设置了十分多的关键帧,这就十分依赖视觉把关键帧导出给前端,光靠前端本人可能很难实现这么丝滑的动画成果。
形式 2:应用 js 实现随机静止
首先确定选项一开始静止的方向
咱们想让选项能够往上下左右随机一个方向开始静止,能够这样实现:
- 生成 0~9 的随机数(其余数值当然也是能够的,确保几率是 50% 即可),判断是否为偶数,如果是偶数往正方向静止,奇数则往负方向静止
function randomDirection(velocity) {const isEventNum = Math.floor(Math.random() * 10) % 2;
return isEventNum == 0 ? velocity : -velocity;
}
const velocityX = randomDirection(0.2)
const velocityY = randomDirection(0.2)
这样每个选项一开始就会有 [x, y], [-x, y],[x, -y], [-x, -y] 四种抉择,实现了初始静止方向的随机。
- 给选项设置最大静止范畴
如图所示,红框是选项的静止范畴(这里只是为了展现,理论范畴会小很多)
如果最大范畴是固定的,静止就显得板滞,能够让这个最大范畴也随机一下
function randomMax(num) {return num - Math.floor(Math.random() * num);
}
randomMax(25)
物体静止到最大范畴时就让其往反方向静止,并且再次调用函数,更新最大范畴的间隔。比方第一次静止,物体 x 轴正方向最大静止范畴是 elemet.originLeft(originLeft 是初始坐标值,这个值始终放弃不变) + 25,达到这个坐标地位后,物体往返 x 轴负方向静止,并且更新最大范畴 x 坐标值,那么下次物体再往 x 轴正方向静止的时候可能静止到 elemet.originLeft + 20 的地位就往负方向静止了,这就实现了静止间隔的随机。
把随机静止的函数封装好,所有的选项都能够应用。
劣势
这种实现办法的益处就是不须要设计师提供反对,毕竟不是每个设计师都可能把本人在 AE 上做的动画成果导出关键帧。
咱们须要做的只是调一下物体静止的速度和最大静止间隔即可。
视频成果地址
实现形式 1:操作 dom
一开始我想到能够应用操作 dom 的形式实现,然而思考了一下,如果开一个定时器,频繁应用 transform 对 dom 进行 translateX,translateY 变换,在 dom 元素比拟多的状况,低端的安卓机子上可能会存在性能问题,为了更好的用户体验,我放弃了这种实现形式。
实现形式 2:canvas 绘图
canvas 绘图的实现形式性能优于操作 dom,晓得了随机静止的思路,实现起来其实并不难,无非就是调用 drawImage()办法绘图,我这里就不再赘述了,只是 canvas 实现有肯定的学习老本,大家能够理解一下,酌情应用。
绘图不清晰
当初的支流手机都采纳高清屏,屏幕上的一个点须要用 3 个像素绘制。为了显示高清页面,咱们的流动都应用宽度为 1125 的 3 倍图做视觉稿,canvas 绘图也须要进行相似的解决,能够参考上面的文章
canvas 绘图含糊解决
canvas 点击事件处理
canvas 绘制的图形不能像 dom 一样绑定一些点击事件,如果须要对绘制的图形进行交互操作如点击,能够依据点击的坐标进行判断
// 把须要点击的元素存在数组中
let clickElements = [a, b, c, d]
function onClick(clientX, clinetY) {clickElements.forEach((element) => {
if (
clinetY > element.top
&& clinetY < element.top + element.height
&& clientX > clientX.left
&& clientX < element.left + element.width
) {// 选中物体,进行一些操作}
})
}
3.3 帧动画
点击 p4 页面的选项,会有一个精灵动画,原理是这样的:
精灵图预览
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
sx,sy 是绘制的 x,y 坐标,比方第一帧绘制图片中的 1 区域,第二帧绘制图片中的 2 区域,以此类推,帧数切换的时候就会产生动画,所以这种成果被称为帧动画。
css 的 animation steps 也是同理。
视频成果地址
3.4 lottie 动画
说到动画成果,不得不提一下 lottie-web,通常设计师都会用 AE 软件制作动画,他们能够把做好的动画导出一份 json 文件,应用 lottie-web 执行,就可能完满的还原动画,应用形式也相当的简略:
lottie.loadAnimation({
renderer: 'svg',
loop: false,
autoplay: false,
container: document.querySelector('.p6_a'),
name: 'p6a',
animationData: p6aJson // 设计师导出的 json
});
lottie-web 能实现的动画成果有:
- 平移
- 放大
- 旋转
- 淡入淡出
- svg 的各种动画
… 等等
根本能满足大部分的动画场景,最大的益处就是可能大量节俭开发工夫以及和设计师联调的工夫。
之前一提起要做动画成果,我想到的就是成果类的实现预计又得花上不少工夫进行开发与调试,应用 lottie-web 的确能够大量晋升效率。
这个库的体积也比拟 mini,只有 67kb 左右,兼容性也比拟好,亲测在安卓 4.4 版本动画也能运行。
lottie-web 的毛病
一些特效类的成果无奈实现
比方这种成果
点击元素须要切换图片的成果不好实现
lottie-web 次要做一些用来展现用的动画,一些须要交互的动画可能要慎重考虑,比方 p3 页面的选项,点击之后须要切换图片这种就不太好做了。如果元素是纯色的能够实现,比方能够让设计师应用 svg 代替 image,而 svg 的色彩能够继承父元素,点击元素之后切换色彩是能够做到的。
3.5 视频动画
一些特效类的成果,能够思考做为背景视频实现,比方第 1 页的转场
视频成果地址
应用视频做动画的益处是成果炫酷,接入老本较低,如果一些动效通过技术手段不好实现,能够思考做成视频接入,这类动效不能有交互操作,所以个别做为背景。
3.6 动画成果实用场景及总结
css 实现动画
如果只是一些简略的动画成果,间接用 css 实现是最不便的。
简单一点的成果最好让设计师提供 keyframes,以及 transition-timing-function,如果无奈提供,能够思考其余计划,不然很可能做进去之后要花费比拟多的工夫与设计师联调动效。
如果在安卓机子呈现性能问题,须要优化一下性能,能够用上面两种形式。
// 形式 1:transform: translate3d(0,0,0);
// 形式 2:will-change: auto;
lottie 动画
可能完满还原设计师在 AE 上制作的动画,大幅度节俭开发,联调动效工夫,罕用于展现类型的动画,须要交互的元素酌情应用,大部分场景举荐应用。
js 实现动画
利用 js 的能力能够实现一些应用 css 不好实现的成果,比方生成随机数,物体重力着落,物体碰撞回弹等物理静止,比方一个篮球运动员在运球,如果能越完满的还原篮球的静止轨迹,动画成果就会显得更实在。
js 实现动画能够有 2 中形式:
- 如果静止的元素不多,并且对性能没有特地高的要求能够应用操作 dom 的形式实现,因为能够不便的绑定点击等事件
- canvas 实现动画动画性能优良,当页面动画元素较多能够思考,然而它有肯定的学习老本,2d 的动画其实也还好,如果要更进一步实现 3d 的成果须要波及到一些图形学的常识,学习曲线比拟平缓。
帧动画
帧动画实现的成果较为天然,各种成果也都能实现,但受到图片大小的限度,比拟实用于小型物体帧数较少的动画,比方题目选项,手势动作等。因为如果帧数过多,图片较大,对手机的渲染有压力。
视频动画
技术上不好实现的特效能够做成视频,然而视频的播放在挪动端往往会遇到一些坑,也要思考视频的大小,按需做预加载,并且在挪动端通常须要点击能力播放视频。
4. 挪动端页面适配
挪动端尽管应用了 rem 布局,但还是有某些非凡场景须要进行适配,比方页面在谷歌 iphone5 模拟器环境中页面下方被截断了一些
在真机上的体现那就更为不堪了。
要适配这种小屏幕的手机,可能咱们会想到应用 css 的媒体查问。通过观察,咱们能够看到元素的间距还是挺大的,能够通过调整间距来达到适配的目标。
coding…
// iphone 5
@media only screen
and (min-device-width : 320px)
and (max-device-height : 568px) {
div1 {margin-top: xxx;}
}
想法很美妙,然而咱们的流动须要在各种环境下投放,在安卓原生浏览器中,底部会带有返回,后退等操作的区域,这无疑让屏幕的显示区域变小了,即便是大屏手机,底部的元素也会被截取局部。而且咱们也只适配了 iphone5 这个尺寸的手机,我意识到市面上手机尺寸繁多,如果呈现了问题就要专门给这个尺寸的手机写个媒体查问,这并不是一种优雅的计划。
应用 flex 布局适配
首先咱们先来理解一下 flex 的一些属性
- flex-grow:定义我的项目的放大比例,默认为 0,及时存在残余空间,也不放大
- flex-shrink:定义我的项目的放大比例,默认为 1,如果空间有余,该我的项目放大,为 0 则不放大
- flex-basis:定义在调配多余空间之前,我的项目占据的主轴空间。浏览器依据这个属性,计算主轴是否有多余空间,默认值为 auto,即我的项目原本的大小
接着察看页面
图中:1,2,3,4 局部能够依据屏幕的尺寸进行动静放大,序号,题目,图片,密封线等元素不要放大,并且题目选项显示区域作为页面最次要的局部应该随着手机屏幕变大而动静调整。想好了思路之后开始着手实现
// 页面应用 flex 布局,并且将主轴设置为垂直方向
.page {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
// 图中标识为 1 的区域应用 div 填充
.div1 {
flex-basis: 0.95rem;
flex-shrink: 1;
}
// 题目显示区域,默认大小为 13.36rem,即便空间有余也不放大,空间残余则变大
.content {
flex-shrink: 0;
flex-grow: 1;
flex-basis: 13.36rem;
}
... 其余元素相似
flex-shrink 也能够定义放大的优先级,比方 div1 的 flex-shrink = 1,div2 的 flex-shrink = 2,则优先放大 div2 的高度
flex-basis 是一个十分要害的属性,通过 flex-basis 浏览器能够更精确的给我的项目调配空间,如果应用高度代替 flex-basis,在 ios 10.3 版本会呈现元素无奈放大的状况。
看看最终成果
联合 autoprefixer,能够让 flex 布局有很好的兼容性,上面咱们看看应用 autoprefixer 生成的兼容性代码 display: -webkit-box 的设施兼容状况
能够看到兼容性曾经十分不错了
总结两种计划
- 媒体查问适宜繁多渠道,比方只在某个 app 内,且呈现布局问题的机型不是很多的状况下应用,操作简略,调整一下呈现问题的元素即可
- flex 布局适宜多种场景,并且通过 autoprefixer 后兼容性较好,举荐应用
5. 视频的预加载
因为流动中有好几个视频做为背景,为了给用户更好的观感体验,开发者通常会对视频进行预加载,上面来谈谈进行视频预加载的两种形式。
形式 1:提前一个页面加载视频
如果你的页面遵循固定的拜访程序,比方 p1 => p2 => p3,你能够思考在拜访 p1 的时候就学生成 p2 的 video 标签,并且给标签增加 preload=”auto” 属性,以此类推,达到一个预加载的目标。然而这种形式限度比拟大。
- 页面拜访程序需固定
- 有些手机浏览器为了用户的流量思考会把 preload 属性强制设置成 none,这就达不到预加载的目标了
形式 2:提前申请视频资源数据
axios({
method: 'get',
url: 'video url',
responseType: 'blob'
}).then(res => {const blobUrl = URL.createObjectURL(res)
// 生成 video 标签,并且设置 src = blobUrl
})
blob 就是视频的原始数据,通过 createObjectURL,咱们能够生成一个 blob url,而后创立 video 标签,这样就能够达到一个预加载的目标。
如果感觉还不够保险,还能够监听 video 标签的 canplaythrough 事件,当浏览器判断视频能够无需缓冲,可能晦涩的播放视频就触发此事件。
this.video.addEventListener('canplaythrough', () => {callback && callback()
});
试想,流动一开始有一个 loading,背地进行视频预加载,加载结束后正式进入页面,这样的用户体验是比拟好的。
这种实现形式能够实用于多种视频播放场景,但值得注意的是如果要申请站外的视频资源,须要解决一下跨域的问题。
视频播放的坑
生成 video 标签之后须要
this.video.load()
load()办法重置媒体成初始化状态,亲测如果在 chrome 中视频播放了屡次,却没有调用 load()办法,可能视频会无奈播放,具体起因我还没理解分明。
在挪动端微信浏览器下,如果没有调用 load()办法,某些 ios 手机无奈触发 canplaythrough 事件。
某些安卓手机播放视频之前会黑屏进行解码,能够在视频下面蒙上第一帧图片,监听视频的 timeupdate 事件,当视频的 currentTime 属性有值的时候证实视频开始播放了,这时能够把图片暗藏。
伪代码实现,可做参考。
6. 学习材料举荐
最初给大家举荐一本书《HTML5 Canvas 核心技术图形动画与游戏开发》,这本书的教学风格是我喜爱的,首先介绍知识点,而后使用这些知识点做 demo,毛病就是代码偏多,知识点解说不够具体