#完整原文地址见简书www.jianshu.com/p/f3b52299a… #更多完整Vue笔记目录敬请见《前端 Web 笔记 汇总目录(Updating)》
#本文内容提要
Vue常规动画写法
Vue过渡动画 常规写法与 例程
上例的另一种实现方式
入场动画
出场动画
出入场动画同时实现时,可以简化以上代码
出场入场帧动画
使用
name=
对动画进行 片段式地 命名对动画类的完全命名
完全命名的方式 使得 容易接入 第三方库
注意以上案例,将
v-if
换成v-show
也是可以正常运行的颜色过渡和位移 动画 结合
控制组合动画时长 以某一类型动画的时间为准
控制组合动画时长 以自定义的时长为准
分别 统一 出入场动画的时延
禁用CSS动画,使用JS动画
最基本的 多个 单元素标签 切换案例
带上入场 退场 动画的 两个单元素标签切换
带上入场 退场 动画的 两个组件 的切换
<component>
组件占位 + is属性 + 双向绑定特性 实现上例效果列表动画
状态动画
####Vue常规动画写法 >**关键帧 --- CSS类 --- data数据 --- DOM的Class属性**
-- 使用@keyframes [关键帧实例名]
配置好关键帧;
-- 使用animation
配置关键帧
以及动画过程到完成
的时延
,
完成动画的定义【写在一个CSS类中(如下的myAnimation
)】;
-- 在data
中定义一个以 上面定义的动画CSS类
实例(如myAnimation
) 为属性值的 数据字段
(如myAnimateData
);
-- 在dom中使用:class=[以 动画CSS类实例 为属性的 数据字段]
,
引用这个数据字段
(myAnimateData
)即可,至此完成动画定义;
-- 数据字段
(如myAnimateData
)中,可以通过对 属性值即动画CSS类
实例的 布尔值的 改变,
去控制动画的开关,如下 配置false 为关:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World! heheheheheheda</title>
<style>
@keyframes leftToRight {
0% {
transform: translateX(-100px);
}
50% {
transform: translateX(-50px);
}
0% {
transform: translateX(0px);
}
}
.myAnimation {
animation: leftToRight 3s;
}
</style>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="heheApp"></div>
</body>
<script>
const app = Vue.createApp({
data() {
return {
myAnimateData: {
myAnimation: false
}
}
},
template: `
<div>
<div :class="myAnimateData">heheheda</div>
</div>`
});
const vm = app.mount('#heheApp');
</script>
</html>
运行效果,没有动画,DOM中也没显示对应的class:
改为true:
<script>
const app = Vue.createApp({
data() {
return {
myAnimateData: {
myAnimation: true
}
}
},
template: `
<div>
<div :class="myAnimateData">heheheda</div>
</div>`
});
const vm = app.mount('#heheApp');
</script>
运行时产生动画:
可以配合按钮和点击事件,动态 交互地 开关动画:
<script>
const app = Vue.createApp({
data() {
return {
myAnimateData: {
myAnimation: true
}
}
},
methods: {
handleClick() {
this.myAnimateData.myAnimation = !this.myAnimateData.myAnimation;
}
},
template: `
<div>
<div :class="myAnimateData">heheheda</div>
<button @click="handleClick">切换</button>
</div>`
});
const vm = app.mount('#heheApp');
</script>
####Vue过渡动画 常规写法与 例程 **-- 使用`transition: [时延] background-color ease;`定义 在一个时延内的`过渡动画`, ` background-color`指定过渡对象是`背景颜色`,`ease`指定为`平缓地进行`; -- 类似上例,在组件的data中,定义一个 对应CSS类的 `Object类型数据字段`,并在其中包含`过渡动画`和`定义好的背景颜色`的数据字段; -- 在`template`中,使用`:class=[类实例名]`引用`data`中的`CSS类实例`即可; -- 可以准备一个`触发事件`,在事件中`反转`两个背景颜色值,由此可实现过渡动画:** ``` Hello World! heheheheheheda .myTransition { transition: 3s background-color ease; } .aquamarine { background-color: aquamarine; } .orange { background-color: orange; } ``` **运行效果:点击按钮 触发事件后 过渡的中途:过渡完毕:**
####上例的另一种实现方式 **-- 定义`css动画类`, 在dom节点直接使用`class=[CSS动画类]`配置上这个CSS动画类; -- `data`中定义`初始背景颜色键值`,打包成`object`类型, 配置到DOM节点的`:style=`上,作`双向绑定`; -- 把`css`中定义的`背景颜色字段`都去掉, 直接写在`触发事件`的方法中,在其中通过更改`绑定的style字段的值`改变背景颜色;** ``` Hello World! heheheheheheda .myTransition { transition: 3s background-color ease; } ``` **运行效果如上例;**
####入场动画 **如下, ``标签定义的三个类, 分别指定 `入场动画`的`开始时`的透明度、 过程持续的`时延 `和 `速度`(ease -- 缓慢)、 入场动画的`结束时`的透明度; `template`中, 使用`<transition>`标签对 包裹 要实现`入场动画`的DOM节点即可;** ``` <head> ... <style> .v-enter-from{ opacity: 0; } .v-enter-active { transition: opacity 3s ease; } .v-enter-to { opacity: 1; } ``` **运行效果,实现入场动画: 初始: 入场中:入场完毕:**
####出场动画 **理同入场动画,其中, `v-leave-from`表示 `出场动画`的`开始时`的透明度、 `v-leave-active`表示过程持续的`时延 `和 `速度`(ease -- 缓慢)、 `v-leave-to`表示出场动画的`结束时`的透明度; 其余步骤 同入场动画:** ``` Hello World! heheheheheheda .v-enter-from{ opacity: 0; } .v-enter-active { transition: opacity 3s ease-out; } .v-enter-to { opacity: 1; } .v-leave-from { opacity: 1; } .v-leave-active { transition: opacity 3s ease-in; } .v-leave-to { opacity: 0; } </style> <script src="https://unpkg.com/vue@next"></script> ``` **运行效果: 初始: 切换中:切换结束:**
####出入场动画同时实现时,可以简化以上代码 **两个`v-xxx-active`可以写到一起, 如果`v-leave-from`指定的值与`v-enter-to`相同(一般都是相同的),那也可以省略它; 以下代码实现的效果,同上:** ``` ... .v-enter-from{ opacity: 0; } .v-enter-active, .v-leave-active { transition: opacity 3s ease-out; } .v-enter-to { opacity: 1; } .v-leave-to { opacity: 0; } ... ```
####出场入场帧动画 **准备好一个`keyframes`,然后把它写在`v-xxx-active`中即可:** ``` ··· @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } .v-enter-active { animation: shake 2s; } .v-leave-active { animation: shake 2s; } ··· ```
####使用`name=`对动画进行 片段式地 命名 **可以在`template`中, 在``标签中,用`name=`指定一个命名, 然后在style中定义css类时, 将之前的`v-xxx-xxx`等定义符换成`[命名]-xxx-xxx`即可:** ``` Hello World! heheheheheheda @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } .heheda-enter-active { animation: shake 2s; } .heheda-leave-active { animation: shake 2s; } ```
####对动画类的完全命名 **即直接在`template`中,在``标签中, 使用`[原css命名] = [新命名]`的方式,对整个CSS类取别名, 用的时候,直接使用新命名即可:** ``` Hello World! heheheheheheda @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } .hihi { animation: shake 2s; } .byebye { animation: shake 2s; } ``` **关键代码:** ``` ... ... .hihi { animation: shake 2s; } .byebye { animation: shake 2s; }
...
**运行效果同上例;**
<br>
####完全命名的方式 使得 容易接入 第三方库
**这里以引入`Animate.css`为例;
官网:https://animate.style/
官网首页如下,右侧列表是各种动画类型,点击可以预览效果:
官网简略引导:
(如下可知,即`框架`已经把`动画`定义`实现`好了,
使用时只用直接使用`定义好的框架`的`样式命名`即可)**
**弹跳效果:**
Hello World! heheheheheheda
```
闪烁效果:
```
template: `
####注意以上案例,将`v-if`换成`v-show`也是可以正常运行的 **区别在于隐藏组件的底层机制不同而已, 在博客笔记[《Vue3 | 条件渲染 与 列表循环渲染》](https://www.jianshu.com/p/695bf35fa466)我们讨论过这个事情;**
####颜色过渡和位移 动画 结合 ``` Hello World! heheheheheheda @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } .v-enter-from { color: red; } .v-enter-active { animation: shake 2s; transition: all 2s ease-in; } .v-leave-active { color: red; animation: shake 2s; transition: all 2s ease-in; } ``` **关键代码: 入场,从红色开始,到黑色(默认颜色); 出场,从黑色(默认颜色)开始,到红色; `all`表示`过渡特性`适配到 所用类型的动画;** ``` .v-enter-from { color: red; } .v-enter-active { animation: shake 2s; transition: all 2s ease-in; } .v-leave-active { color: red; animation: shake 2s; transition: all 2s ease-in; } ```
####控制组合动画时长 以某一类型动画的时间为准 **在``标签上, 写上`type="transition"`,则组合动画的时长 以过渡动画 为准; 写上`type="animation`,则组合动画的时长 以普通类型动画 为准;** 如下案例,写上`type="transition"`, 则组合动画在 过渡动画执行完毕(即3s)之后, 会立即停止 正在运行的 所有动画: ``` Hello World! heheheheheheda @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } .v-enter-from { color: red; } .v-enter-active { animation: shake 6s; transition: all 3s ease-in; } .v-leave-active { color: red; animation: shake 6s; transition: all 3s ease-in; } ```
####控制组合动画时长 以自定义的时长为准 **在``标签上, 写上`:duration="[动画时长 / ms]"`,则组合动画的时长 以这里定义的数据 为准, 无论CSS类中,定义了多长的时延:** ``` template: `
####分别 统一 出入场动画的时延 **`:duration="[{enter: [入场动画时延], leave:[出场动画时延]}]"`** ``` template: `
####禁用CSS动画,使用JS动画 **-- 使用`:css="false"`禁用CSS动画,使得CSS动画都失效(如果有定义的话); -- `@before-enter = "[方法名]"` 定义 `入场就绪` 及 `开始前` 状态的动画回调方法; -- `@enter = "[方法名]"` 定义 `动画运行时` 状态的 回调方法; -- `@after-enter="[方法名]"`定义 `动画结束时` 状态的 回调方法; -- `done();` 通知架构动画已经结束,可以回调 `handleEnterEnd(el)`了; 【注意,以上是`入场动画`的钩子回调, 把`enter`换成`leave`,即是对应的`退场动画`的钩子回调】**
如下代码,
handleBeforeEnter(el)
指定 初始动画颜色 为 橙色;
handleEnterActive(el, done)
使得 动画在 橙色 与 蓝色之间 动态切换,
配合 setInterval(() => {}, 1000)
时隔一秒切换一次;
clearInterval(animation);
配合 setTimeout(() => {}, 5000)
使得5秒后关闭动画;
随后调用done();
通知架构 动画已经结束,可以回调 handleEnterEnd(el)
了;
handleEnterEnd(el)
调试性地 弹出一个弹窗;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World! heheheheheheda</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="heheApp"></div>
</body>
<script>
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show;
},
handleBeforeEnter(el) {
el.style.color = "orange";
},
handleEnterActive(el, done) {
const animation = setInterval(() => {
const color = el.style.color;
if(color === "orange") {
el.style.color = 'blue';
} else {
el.style.color = 'orange';
}
}, 1000)
setTimeout(() => {
clearInterval(animation);
done();
}, 5000)
},
handleEnterEnd(el) {
alert("动画结束!!!");
}
},
template: `
<div>
<transition
:css="false"
@before-enter = "handleBeforeEnter"
@enter="handleEnterActive"
@after-enter="handleEnterEnd">
<div v-if="show">heheda</div>
</transition>
<button @click="handleClick">切换</button>
</div>`
});
const vm = app.mount('#heheApp');
</script>
</html>
运行效果:
####最基本的 多个 单元素标签 切换案例 ``` Hello World! heheheheheheda ```
####带上入场 退场 动画的 两个单元素标签切换 **-- css块上面已经讲过了,这里主要是看`mode="in-out"`; 这里指定为`mode="in-out"`的话, 则切换时 先进行入场节点的 入场 及其 动画【in】, 再进行退场节点 的 入场 及其 动画【out】; 【缺点:入场 与 退场的节点,在入场动画完毕前,会同框】
-- 如 指定为`mode="out-in"`,则顺序与上相反; 【特性:入场 与 退场的节点 不会同框】
-- `appear`特性:初始加载节点的时候,就会启动`入场动画`, 没有加这个标签,`入场动画`需要触发才会启动;** ``` Hello World! heheheheheheda .v-leave-to { opacity: 0; } .v-enter-from { opacity: 0; } .v-enter-active, .v-leave-active { transition: opacity 2s ease-in; } .v-leave-from, .v-enter-to { opacity: 1; } ```
####带上入场 退场 动画的 两个组件 的切换 **基于上例进行小改动即可,其原理是类似的, 以下代码实现的效果 同上例;** ``` Hello World! heheheheheheda .v-leave-to { opacity: 0; } .v-enter-from { opacity: 0; } .v-enter-active, .v-leave-active { transition: opacity 2s ease-in; } .v-leave-from, .v-enter-to { opacity: 1; } ```
####``组件占位 + is属性 + 双向绑定特性 实现上例效果 **-- `data`字段 存储要展示的子组件 的 组件名; -- 点击事件中 更改`data`字段 以 更换 展示的子组件, 同时产生`组件的 入场退场`,触发相关动画; -- `template`中 使用 ``标签占位, 使用`is`控制要展示的组件, 指定值为 `data`字段;**
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World! heheheheheheda</title>
<style>
.v-leave-to {
opacity: 0;
}
.v-enter-from {
opacity: 0;
}
.v-enter-active,
.v-leave-active {
transition: opacity 2s ease-in;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
</style>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="heheApp"></div>
</body>
<script>
const ComponentA = {
template: `<div>heheda</div>`
}
const ComponentB = {
template: `<div>lueluelue</div>`
}
const app = Vue.createApp({
data() {
return {
component : 'component-a'
}
},
methods: {
handleClick() {
if (this.component === 'component-a') {
this.component = 'component-b';
} else {
this.component = 'component-a';
}
},
},
components: {
'component-a': ComponentA,
'component-b': ComponentB,
},
template: `
<div>
<transition mode="out-in" appear>
<component :is="component">
</transition>
<button @click="handleClick">切换</button>
</div>`
});
const vm = app.mount('#heheApp');
</script>
</html>
运行效果同上例;
####列表动画 >**即列表增删元素时,产生的`入场 和 出场动画`;**
主要是对itemView
、v-for
等标签套上<transition-group>
这个特殊动画标签,
然后点击事件时使用unshift
在队头添加元素,
其他的定义跟普通动画差不多;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World! heheheheheheda</title>
<style>
.v-enter-from {
opacity: 0;
transform: translateY(30px);
}
.v-enter-active {
transition: all .5s ease-in;
}
.v-enter-to {
opacity: 1;
transform: translateY(0);
}
.v-move {
transform: all .5s ease-in;
}
.list-item {
display: inline-block;
margin-right: 10px;
}
</style>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="heheApp"></div>
</body>
<script>
const app = Vue.createApp({
data() {
return {
list : [1, 2, 3]
}
},
methods: {
handleClick() {
this.list.unshift(this.list.length + 1);
},
},
template: `
<div>
<transition-group>
<span
class="list-item"
v-for="item in list"
:key = "item">{{item}}</span>
</transition-group>
<button @click="handleClick">增加</button>
</div>`
});
const vm = app.mount('#heheApp');
</script>
</html>
运行效果:
####状态动画 >**显示一个值【状态】 到 另外一个值【状态】 的变化过程, 类似于Android的`ValueAnimator`;** ``` Hello World! heheheheheheda ``` **运行效果:**
####