大前端开发中经常会遇到动画的开发,那么什么是动画?在物理学中运动就是研究物体在时间维度和空间维度上改变的现象,所以动画也一样,动画主要研究2个因素,发生运动物体的 时间和 空间。
Web前端开发中的动画
在 Web 前端开发中实现动画有2种方式。要么依靠 CSS 实现动画,要么依靠 JS 控制实现动画。
CSS 实现动画
首先要说 CSS 中的4个概念:animation、transition、transform、translate
| 属性 | 含义 | | :---: | :---: | | transition(过度动画) | 用于设置元素的样式过渡效果,和 animation 有类似的效果,但存在使用场合有着较大差别 | | transform(变形) | 用于设置元素的旋转、位移、缩放。和设置元素的动画并没直接关系,就跟写 css 属性一样 | | translate(移动) | 用于设置元素的位置,就是 transform 的一个属性 | | animation(动画) | 用于设置怨毒的动画属性,它是一个简写,有6个属性值 |
transition 字面意思,过渡是指元素从属性a的某个值过渡到属性a的另一个值,这就是一个状态的改变,但是需要一个条件来触发从而发生这种转变,比如 &:hover,&:checked,&:focus、媒体查询或者 JS
#box {
height: 100px;
width: 100px;
background: green;
transition: transform 1s ease-in 1s;
}
#box:hover{
transform: rotate(180deg) scale(0.5,0.5);
transform: translateX(100px);
transform: translateY(100px) translateX(100px) scale(0.5, 0.5);
}
<div id="box"></div>
分析:给 div 添加了一个过渡动画,动画指定了 transform 动画,触发时机为当鼠标移上去的时候。因此当鼠标移入的时候元素的 transform 属性发生变化,那么这个时候触发了 transition 动画,当鼠标移除的时候也产生了 transform 的变化,因此还是会触发 transition,产生动画。 上面设置了3个 transform 只有最后一个生效 因此 transition 产生动画的条件是设置的 property 发生变化,这种动画的特点是需要一个驱动力去触发。因此就存在一些缺点: 1. 需要事件触发,没法在网页加载时自动发生 2. 是一次性的,不能重复发生,除非再次触发 3. 只可以定义开始状态和结束状态,不能定义中间状态,因此没有丰富的动画空间 4. 一条 transition 规则,只能定义一个属性的变化,不能涉及多个属性
语法:transition:property duration timing-function delay
![9f69092406fa263872a6151f9872962a.png](https://i-blog.csdnimg.cn/blog_migrate/ce7159e76991324782ea22f56552f15e.jpeg)
animation
animation 总体来说是对 transition 的增强,不再受限于触发时机和动画的属性值。
.box {
height: 100px;
width: 100px;
border: 15px solid black;
animation: changebox 4s ease-in-out 1s 1 alternate running forwards;
}
.box:hover {
animation-play-state: paused;
}
@keyframes changebox {
10% {
background: red;
}
50% {
width: 80px;
}
70% {
border: 15px solid yellow;
}
100% {
width: 180px;
height: 180px;
}
}
<div class="box"></div>
animation: animation-name, animation-duration, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, animation-fill-mode
|属性 | 含义 | | :----: | :----: | | animation-name | 用来调用@keyframes定义好的动画,与@keyframes定义的动画名称一致 | | animation-duration | 指定元素播放动画所持续的时间 | | animation-timing-function | 指定速度效果的速度曲线,是针对每一个小动画所在时间范围内的变化频率| | animation-delay| 定义在执行动画之前的等待时间| | animation-iteration-count| 定义动画的播放次数,可选具体次数或者无限次(infinite)| | animation-direction |设置动画播放方向:normal(按时间轴顺序)、alternate(轮流,即来回往复进行) | | animation-play-state| 控制元素的播放状态:running(继续)、paused(暂停)| | animation-fill-mode | 控制动画结束后元素的样式,有4个值: none(回到动画之前的状态)、forwards(元素停留在动画结束后的状态)、backwords(动画回到第一帧的状态)、both(根据 animation-direction 轮流应用 forwards 和 backwords 规则)。注意与 iteration-count 不要冲突|
总结:单个动画效果、简单的由 transtion 实现,复杂的用 animation 实现。animation 出现后市面上出现了很多这种 css 动画库,其中我在使用 animate.css 推荐小伙伴们使用下
JS 实现动画
大家用 JS 写动画立马想到的是 setTimeout 和 setInterval,但是较好的动画体验是保持在 60fps 最好,上面的2个 api 由于会受到 runloop 的影响,并不会特别准时,JS 有个 requestAnimationFrame api 可以保持动画在 60fps, JS 实现动画的本质就是控制元素在时间和空间上的变化的研究。
假如要实现一个匀速直线动画,让一个 div 在 3秒内在水平方向上从向由右移动500px。那么如何实现,先从物理问题上解决吧。
总时间: 3s 总位移: 500px 那么每秒移动多少 500px/3s 我们设计一个 JS 函数。4个参数, 属性开始值,属性结束值,动画执行时间,回调函数
大体思路是:外界传入上面4个参数,我们可以记录函数调用刚开始的时刻也就是开始时间(start),然后通过 performance.now() 拿到当前时间(now),然后 period = (now-start) 就是经过的时间。然后通过 period/time 就是时间的进度百分比,拿这个百分比再去乘以总的属性值差就是当前的属性值,然后将计算结果实时调用回调函数(这个回调函数就是指定这个属性值如何应用到动画元素上)
/**
* 执行补间动画方法
*
* @param {Number} start 开始数值
* @param {Number} end 结束数值
* @param {Number} time 补间时间
* @param {Function} callback 每帧回调
* @param {Function} timing 速度曲线,默认匀速
*/
function animate(start, end, time, callback, timing = t => t) {
let startTime = performance.now() // 设置开始的时间戳
let period = end - start // 拿到数值差值
// 创建每帧之前要执行的函数
function loop() {
liveAnimationFunction = requestAnimationFrame(loop) // 下一调用每帧之前要执行的函数
const passTime = performance.now() - startTime // 获取当前时间和开始时间差
let per = passTime / time // 计算当前已过百分比
if (per >= 1) { // 判读如果已经执行
per = 1 // 设置为最后的状态
cancelAnimationFrame(raf) // 停掉动画
}
const pass = period * timing(per) // 通过已过时间百分比*开始结束数值差得出当前的数值
callback(pass)
}
let liveAnimationFunction = requestAnimationFrame(loop) // 下一阵调用每帧之前要执行的函数
}
function doMove(easing) {
asing])
}
function move(box, value) {
box.style.transform = `translateX(${value}px)`
}
Native 端动画(iOS为例)
其实动画的本质就是元素时间和空间上发生变化的研究。在 web 前端如此,在 native 端也是如此,不过就是换了一些 api 如此。
举个例子,就拿上面所说的水平位移为例,下面给 iOS 的原生代码
//CALayer 层动画
CABasicAnimation *positionAnimation = [CABasicAnimation animation];
//指定动画路径是水平方向x轴
positionAnimation.keyPath = @"position.x";
//指定位移距离
positionAnimation.toValue = @1000;
//下面2行代码让动画停留在动画结束的位置
positionAnimation.fillMode = kCAFillModeForwards;//(效果完全等同于 css 中的 animation-fill-mode属性)
positionAnimation.removedOnCompletion = NO;
[self.animationView.layer addAnimation:positionAnimation forKey:nil];
css 中的 animation-fill-mode(控制动画结束后元素的样式,有4个值: none(回到动画之前的状态)、forwards(元素停留在动画结束后的状态)、backwords(动画回到第一帧的状态)、both(根据 animation-direction 轮流应用 forwards 和 backwords 规则))和 native(iOS)端的 fillMode 属性一致。
下面提出 iOS 端的 fillMode 取值选项
/* `fillMode' options. */
CA_EXTERN CAMediaTimingFillMode const kCAFillModeForwards
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CAMediaTimingFillMode const kCAFillModeBackwards
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CAMediaTimingFillMode const kCAFillModeBoth
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CAMediaTimingFillMode const kCAFillModeRemoved
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
看得出来 fillMode web 端和 native 端是一模一样的。
再举个例子,平时我们有可能画一根横线,在 web 端 和 native 端都存在 view 这样的概念,在 web 端可能会是一个 div(高度设置为1)或者是 canvas 实现(canvas 拿到当前上下文、绘制路径、关闭路径、填充颜色)。在 native 端也一样,开启绘图上下文、拿到上下对象、绘制路径、上色、关闭上下文。
所以其他具体的例子也就不举了,本质上大前端的所有动画干的事情都一样,所以我们需要处理好时间和位置的关系。