# 1.install & import
/* 需要使用到的库 */ | |
npm install animate.css | |
npm install gsap | |
npm install lodash | |
main.js | |
import "animate.css" // 引入到 main.js |
# 2.animate 基本使用
// App.vue | |
<template> | |
<div class="app"> | |
<div><button @click="isShow = !isShow">显示/隐藏</button></div> | |
<transition name="neko"> | |
<h2 class="title" v-if="isShow">Hello World</h2> | |
</transition> | |
</div> | |
</template> | |
<script> | |
export default { | |
data() { | |
return { | |
isShow: true, | |
}; | |
}, | |
}; | |
</script> | |
<style scoped> | |
.title { | |
display: inline-block; | |
background-color: pink; | |
} | |
.animate__flipInY { | |
animation-direction: reverse; | |
} | |
.neko-enter-active { | |
animation: bounceInUp 1s ease-in; | |
} | |
.neko-leave-active { | |
animation: bounceInUp 1s ease-in reverse; | |
} | |
</style> |
参考结果:
# 3.animate attribute
动画名参考:https://animate.style
<template> | |
<div class="app"> | |
<div><button @click="isShow = !isShow">显示/隐藏</button></div> | |
<!-- 后面的 animate__fadeInDown 动画名基础上必须加上 animate__animated --> | |
<!-- 动画名参考网址: --> | |
<transition | |
enter-active-class="animate__animated animate__fadeInDown" | |
leave-active-class="animate__animated animate__flipInY" | |
> | |
<h2 class="title" v-if="isShow">Hello World</h2> | |
</transition> | |
</div> | |
</template> | |
<script> | |
export default { | |
data() { | |
return { | |
isShow: true, | |
}; | |
}, | |
}; | |
</script> | |
<style scoped> | |
.title { | |
display: inline-block; | |
background-color: pink; | |
} | |
.animate__flipInY { | |
animation-direction: reverse; | |
} | |
</style> |
参考结果:
- attribute:自定义优先级更高
- enter-from-class
- enter-active-class
- enter-to-class
- leave-from-class
- leave-active-class
- leave-to-class
# 4.gsap 生命周期钩子
<template> | |
<div class="app"> | |
<div><button @click="isShow = !isShow">显示/隐藏</button></div> | |
<transition | |
@before-enter="beforeEnter" | |
@enter="enter" | |
@after-enter="afterEnter" | |
@before-leave="beforeLeave" | |
@leave="leave" | |
@afterLeave="afterLeave" | |
> | |
<h2 class="title" v-if="isShow">你好呀,李银河</h2> | |
</transition> | |
</div> | |
</template> | |
<script> | |
export default { | |
data() { | |
return { | |
isShow: true, | |
}; | |
}, | |
methods: { | |
beforeEnter() { | |
console.log("beforeEnter"); | |
}, | |
enter() { | |
console.log("enter"); | |
}, | |
afterEnter() { | |
console.log("afterEnter"); | |
}, | |
beforeLeave() { | |
console.log("beforeLeave"); | |
}, | |
leave() { | |
console.log("leave"); | |
}, | |
afterLeave() { | |
console.log("afterLeave"); | |
}, | |
}, | |
}; | |
</script> | |
<style scoped> | |
.title { | |
display: inline-block; | |
} | |
</style> |
参考结果:
- @before-enter:进入动画之前
- @enter:进入动画
- @after-enter:进入动画结束
- @enter-cancelled:进入取消
- @before-leave:在离开之前
- @leave:离开动画
- @after-leave:离开之后
- @leave-cancelled:离开取消
- :css:让 Vue 跳过 CSS 的检测,性能略高一些,避免过渡过程中 CSS 规则的影响
- v-move:对于哪些其他需要移动的节点是没有动画的,就使用新增的 v-move 的 class 来完成动画
# 5.gsap 基本使用
<template> | |
<div class="app"> | |
<div><button @click="isShow = !isShow">显示/隐藏</button></div> | |
<transition @enter="enter" @leave="leave" :css="false"> | |
<h2 class="title" v-if="isShow">Hello World</h2> | |
</transition> | |
</div> | |
</template> | |
<script> | |
import gsap from "gsap"; | |
export default { | |
data() { | |
return { | |
isShow: true, | |
}; | |
}, | |
methods: { | |
enter(el, done) { | |
console.log("enter"); | |
gsap.from(el, { | |
scale: 0, | |
color: "red", | |
x: 100, | |
y: -200, | |
onComplete: done, // 需要进行 done 回调 | |
}); | |
}, | |
leave(el, done) { | |
console.log("leave"); | |
gsap.to(el, { | |
scale: 0, | |
x: 100, | |
y: -100, | |
onComplete: done, | |
}); | |
}, | |
}, | |
}; | |
</script> | |
<style scoped> | |
.title { | |
display: inline-block; | |
} | |
</style> |
参考结果:
# 6.gsap 实现数字递增效果
<template> | |
<div class="app"> | |
<input type="number" step="100" v-model="counter" /> | |
<h2>当前计数: {{ showNumber.toFixed(0) }}</h2> | |
</div> | |
</template> | |
<script> | |
import gsap from "gsap"; | |
export default { | |
data() { | |
return { | |
counter: 0, | |
showNumber: 0, | |
}; | |
}, | |
watch: { | |
counter(newValue) { | |
// 在 duration 的时间段一点点变化 | |
gsap.to(this, { duration: 1, showNumber: newValue }); | |
}, | |
}, | |
}; | |
</script> | |
<style scoped></style> |
参考结果:
![animate_gsap_progressive_increase](animate_gsap_progressive_increase .gif)
# 7.gsap 列表交替
// App | |
<template> | |
<div> | |
<button @click="addNum">添加数字</button> | |
<button @click="removeNum">删除数字</button> | |
<button @click="shuffleNum">数字洗牌</button> | |
<transition-group tag="p" name="digitalList"> | |
<span v-for="item in numbers" :key="item" class="item"> | |
{{ item }} | |
</span> | |
</transition-group> | |
</div> | |
</template> | |
<script> | |
import _ from "lodash"; | |
export default { | |
data() { | |
return { | |
numbers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], | |
numCounter: 10, | |
}; | |
}, | |
methods: { | |
addNum() { | |
// 随机插入数字 | |
this.numbers.splice(this.randomIndex(), 0, this.numCounter++); | |
}, | |
// 随机删除数字 | |
removeNum() { | |
this.numbers.splice(this.randomIndex(), 1); | |
}, | |
shuffleNum() { | |
console.log(_); // 打印一下 | |
this.numbers = _.shuffle(this.numbers); | |
}, | |
randomIndex() { | |
// 定义随机插入索引 | |
return Math.floor(Math.random() * this.numbers.length); | |
}, | |
}, | |
}; | |
</script> | |
<style scoped> | |
.item { | |
margin-right: 10px; | |
display: inline-block; | |
} | |
.digitalList-enter-from, | |
.digitalList-leave-to { | |
opacity: 0; | |
/* 从下方往上显示 */ | |
transform: translateY(30px); | |
} | |
.digitalList-enter-active, | |
.digitalList-leave-active { | |
transition: all 1s ease; | |
} | |
/* 移除是依然占据位置,所以在移除时设置绝对定位 脱离标准流 */ | |
.digitalList-leave-active { | |
position: absolute; | |
} | |
/* v-move 给需要移动的节点设置动画 */ | |
.digitalList-move { | |
/* transform 不写值 交给 Vue 处理就行了 */ | |
transition: transform 1s ease; | |
} | |
</style> |
参考结果:
# 8. 列表的交错过渡案例
// App | |
<template> | |
<div> | |
<input v-model="keyword" /> | |
<transition-group | |
tag="ul" | |
name="why" | |
:css="false" | |
@before-enter="beforeEnter" | |
@enter="enter" | |
@leave="leave" | |
> | |
<!-- 给 li 绑定:data-index 属性 --> | |
<li v-for="(item, index) in showNames" :key="item" :data-index="index"> | |
{{ item }} | |
</li> | |
</transition-group> | |
</div> | |
</template> | |
<script> | |
import gsap from "gsap"; | |
export default { | |
data() { | |
return { | |
names: [ | |
"椎名真白", | |
"楪祈", | |
"樱岛麻衣", | |
"牧濑红莉栖", | |
"奈亚子", | |
"薇尔莉特", | |
"土间埋", | |
"立华奏", | |
"阎魔爱", | |
"小鸟游六花", | |
"和泉纱雾", | |
"香风智乃", | |
"伊莉雅", | |
"祢豆子", | |
"爱蜜莉雅", | |
"雷姆", | |
"saber", | |
"远坂凛", | |
"夜刀神十香", | |
"四糸乃", | |
"时崎狂三", | |
"五河琴里", | |
"鸢一折纸", | |
"亚丝娜", | |
"茵蒂克丝", | |
"铃音", | |
"萌王", | |
"入间", | |
"梅普鲁", | |
], | |
keyword: "", | |
}; | |
}, | |
computed: { | |
showNames() { | |
// 筛选 names 数组里是否有从 input value 里面获取的字符串 再判断是否存在 再显示 | |
return this.names.filter((item) => item.indexOf(this.keyword) !== -1); | |
}, | |
}, | |
methods: { | |
// 进入之前回调 | |
beforeEnter(el) { | |
el.style.opacity = 0; | |
el.style.height = 0; | |
}, | |
enter(el, done) { | |
gsap.to(el, { | |
opacity: 1, | |
height: "1.5em", | |
delay: el.dataset.index * 0.05, // 根据索引值 * 0.5 的时间依次变化 | |
onComplete: done, | |
}); | |
}, | |
leave(el, done) { | |
gsap.to(el, { | |
opacity: 0, | |
height: 0, | |
delay: el.dataset.index * 0.05, | |
onComplete: done, | |
}); | |
}, | |
}, | |
}; | |
</script> | |
<style scoped></style> |
参考结果: