Vue3官网-过渡&动画(二)gsap、列表过渡(<transition-group>、tag、FLIP技术、v-move、动态过度、可复用过渡)、状态过渡(第三方库gsap、把过渡放在子组件)
文章目录
总结:
补充
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
truthy(真值):在 JavaScript 中,truthy(真值)指的是在布尔值上下文中,转换后的值为真的值。所有值都是真值,除非它们被定义为 假值(即除
false
、0
、""
、null
、undefined
和NaN
以外皆为真值)。括号内都是假值falsy。.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()
==Event.preventDefault方法取消浏览器对当前事件的默认行为。==比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了;再比如,按一下空格键,页面向下滚动一段距离,使用这个方法以后也不会滚动了。该方法生效的前提是,事件对象的cancelable属性为true,如果为false,调用该方法没有任何效果。
vue:用组件(app.component)构建一个模板(template),并反复使用模板
父组件、子组件
const app = Vue.createApp({ components: { 'component-a': ComponentA, 'component-b': ComponentB } })
上面代码中app为父组件,ComponentA和ComponentB为子组件
Vue中美元$符号的意思
- 除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀
$
,以便与用户定义的属性区分开来。- vue中$refs的作用?
- 当我们需要获取一个dom元素进行操作的时候 可以利用原生js的getElementByXxx 而在vue中可以设置refs来获取这个dom元素
第三方网站
- greensock API(GSAP):是一套用于所有主流浏览器中制作高性能html5动画的工具。他们有一个很棒的 ease visualizer,帮助你建立精心制作的画架。
- GSAP的全名是==GreenSock Animation Platform,这个名字是有些怪异(官网还在各种安利你加入它的Club),但它的确是一个从flash时代一直发展到今天的专业动画库。==参照CSDN:https://blog.csdn.net/weixin_39939012/article/details/80833073
- animate.css :集成第三方 CSS 动画库
- CSS Triggers :来查看哪些属性会在动画时触发重绘。这个网站是用来告诉开发者不同内核浏览器对css属性修改的
重绘
/回流
情况,开发者知道了这些细节可以提高页面性能。JavaScript钩子
- 添加
:css="false"
,也会让 Vue 会跳过 CSS 的检测,除了性能略高之外,这可以避免过渡过程中 CSS 规则的影响。列表过渡
概述
- 默认情况下,它不会渲染一个包裹元素,但是你可以通过
tag
attribute 指定渲染一个元素。(例如<p>指段落,<ul>指网络链接)- 过渡模式(out-in,in-out)不可用,因为我们不再相互切换特有的元素。
- 内部元素总是需要提供唯一的
key
attribute 值。- CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。
列表的进入/离开过渡
补充
splice()方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,该方法会改变原数组。
arr.splice(start, count, addElement1, addElement2, ...);
splice的第一个参数是删除的起始位置(从0开始),第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。
列表:使用v-for
列表过渡:使用
<transition-group>
组件列表的移动过渡
FLIP技术
v-move
类
它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过
name
attribute 来自定义前缀,也可以通过move-class
attribute 手动设置。.flip-list-move { transition: transform 0.8s ease; }
列表的交错过渡
- 通过 data attribute 与 JavaScript 通信,就可以实现列表的交错过渡:
- 数据属性指:el.dataset.index之类
可复用的过渡
- 将
<transition>
或者<transition-group>
作为根组件,然后将任何子组件放置在其中就可以了。- 使用 template
动态过度
首先,在 Vue 中即使是过渡也是数据驱动的!动态过渡最基础的例子是通过
name
attribute 来绑定动态的值。<transition :name="transitionName"> <!-- ... --> </transition>
其次,尽管所有过渡 attribute 都可以动态绑定,我们可用的不仅有 attribute。因为事件钩子是方法,它们可以访问任何上下文中的数据,这意味着根据组件的状态不同,你的 JavaScript 过渡可以有不同的表现。
最后创建动态过渡的最终方案是组件通过接受 prop 来动态修改之前的过渡。
状态过渡
- 数据元素本身动效
- 数据元素
- 数字和运算
- 颜色的显示
- SVG 节点的位置
- 元素的大小和其他的 property
- 这些数据要么本身就以数值形式存储,要么可以转换为数值。有了这些数值后,我们就可以结合 Vue 的响应性和组件系统,使用第三方库(gsap)来实现切换元素的过渡状态。
把过渡放在组件里
管理太多的状态过渡会迅速增加组件实例的复杂度,幸好很多的动画可以提取到专用的子组件中。
如下面所示,所有的数值过渡都自动存放在’animated-integer’组件中
app.component('animated-integer', {
1. 列表过渡
目前为止,关于过渡我们已经讲到:
- 单个节点
- 多个节点,每次只渲染一个
那么怎么同时渲染整个列表,比如使用 v-for
?在这种场景下,我们会使用 <transition-group>
组件。在我们深入例子之前,先了解关于这个组件的几个特点:
- 默认情况下,它不会渲染一个包裹元素,但是你可以通过
tag
attribute 指定渲染一个元素。 - 过渡模式不可用,因为我们不再相互切换特有的元素。
- 内部元素总是需要提供唯一的
key
attribute 值。 - CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。
列表的进入/离开过渡
现在让我们由一个简单的例子深入,进入和离开的过渡使用之前一样的 CSS 类名。
<div id="list-demo">
<button @click="add">Add</button>
<button @click="remove">Remove</button>
<transition-group name="list" tag="p">
<span v-for="item in items" :key="item" class="list-item">
{{ item }}
</span>
</transition-group>
</div>
const Demo = {
data() {
return {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
nextNum: 10
}
},
methods: {
randomIndex() {
return Math.floor(Math.random() * this.items.length)
},
add() {
this.items.splice(this.randomIndex(), 0, this.nextNum++)
},
remove() {
this.items.splice(this.randomIndex(), 1)
}
}
}
Vue.createApp(Demo).mount('#list-demo')
.list-item {
display: inline-block;
margin-right: 10px;
}
.list-enter-active,
.list-leave-active {
transition: all 1s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateY(30px);
}
这个例子有个问题,当添加和移除元素的时候,周围的元素会瞬间移动到他们的新布局的位置,而不是平滑的过渡,我们下面会解决这个问题。
列表的移动过渡
<transition-group>
组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move
类,它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过 name
attribute 来自定义前缀,也可以通过 move-class
attribute 手动设置。
这个类主要用于指定过渡 timing 和 easing 曲线,如下所示:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
<div id="flip-list-demo">
<button @click="shuffle">Shuffle</button>
<transition-group name="flip-list" tag="ul">
<li v-for="item in items" :key="item">
{{ item }}
</li>
</transition-group>
</div>
const Demo = {
data() {
return {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
},
methods: {
shuffle() {
this.items = _.shuffle(this.items)
}
}
}
Vue.createApp(Demo).mount('#flip-list-demo')
.flip-list-move {
transition: transform 0.8s ease;
}
这个看起来很神奇,其实 Vue 内部使用了一个叫 FLIP 的动画技术,它使用 transform 将元素从之前的位置平滑过渡新的位置。
我们将之前实现的例子和这个技术结合,使我们列表的一切变动都会有动画过渡。
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
<div id="list-complete-demo" class="demo">
<button @click="shuffle">Shuffle</button>
<button @click="add">Add</button>
<button @click="remove">Remove</button>
<transition-group name="list-complete" tag="p">
<span v-for="item in items" :key="item" class="list-complete-item">
{{ item }}
</span>
</transition-group>
</div>
const Demo = {
data() {
return {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
nextNum: 10
}
},
methods: {
randomIndex() {
return Math.floor(Math.random() * this.items.length)
},
add() {
this.items.splice(this.randomIndex(), 0, this.nextNum++)
},
remove() {
this.items.splice(this.randomIndex(), 1)
},
shuffle() {
this.items = _.shuffle(this.items)
}
}
}
Vue.createApp(Demo).mount('#list-complete-demo')
.list-complete-item {
transition: all 0.8s ease;
display: inline-block;
margin-right: 10px;
}
.list-complete-enter-from,
.list-complete-leave-to {
opacity: 0;
transform: translateY(30px);
}
.list-complete-leave-active {
position: absolute;
}
TIP
需要注意的是使用 FLIP 过渡的元素不能设置为
display: inline
。作为替代方案,可以设置为display: inline-block
或者将元素放置于 flex 布局中。
FLIP 动画不仅可以实现单维度的过渡,多维网格种的元素也同样可以过渡:
TODO:示例
列表的交错过渡
通过 data attribute 与 JavaScript 通信,就可以实现列表的交错过渡:
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.4/gsap.min.js"></script>
<div id="demo">
<input v-model="query" />
<transition-group
name="staggered-fade"
tag="ul"
:css="false"
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
>
<li
v-for="(item, index) in computedList"
:key="item.msg"
:data-index="index"
>
{{ item.msg }}
</li>
</transition-group>
</div>
const Demo = {
data() {
return {
query: '',
list: [
{ msg: 'Bruce Lee' },
{ msg: '*' },
{ msg: 'Chuck Norris' },
{ msg: 'Jet Li' },
{ msg: 'Kung Fury' }
]
}
},
computed: {
computedList() {
var vm = this
return this.list.filter(item => {
return item.msg.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1
})
}
},
methods: {
beforeEnter(el) {
el.style.opacity = 0
el.style.height = 0
},
enter(el, done) {
gsap.to(el, {
opacity: 1,
height: '1.6em',
delay: el.dataset.index * 0.15,
onComplete: done
})
},
leave(el, done) {
gsap.to(el, {
opacity: 0,
height: 0,
delay: el.dataset.index * 0.15,
onComplete: done
})
}
}
}
Vue.createApp(Demo).mount('#demo')
可复用的过渡
过渡可以通过 Vue 的组件系统实现复用。要创建一个可复用过渡组件,你需要做的就是将 <transition>
或者 <transition-group>
作为根组件,然后将任何子组件放置在其中就可以了。
TODO:使用 Vue3 重构
使用 template 的简单例子:
Vue.component('my-special-transition', {
template: '\
<transition\
name="very-special-transition"\
mode="out-in"\
@before-enter="beforeEnter"\
@after-enter="afterEnter"\
>\
<slot></slot>\
</transition>\
',
methods: {
beforeEnter(el) {
// ...
},
afterEnter(el) {
// ...
}
}
})
函数式组件更适合完成这个任务:
Vue.component('my-special-transition', {
functional: true,
render: function(createElement, context) {
var data = {
props: {
name: 'very-special-transition',
mode: 'out-in'
},
on: {
beforeEnter(el) {
// ...
},
afterEnter(el) {
// ...
}
}
}
return createElement('transition', data, context.children)
}
})
动态过渡
在 Vue 中即使是过渡也是数据驱动的!动态过渡最基础的例子是通过 name
attribute 来绑定动态的值。
<transition :name="transitionName">
<!-- ... -->
</transition>
当你已经定义了 Vue 的过渡类约定,并希望可以快速切换它们的时候,这会非常有用。
尽管所有过渡 attribute 都可以动态绑定,我们可用的不仅有 attribute。因为事件钩子是方法,它们可以访问任何上下文中的数据,这意味着根据组件的状态不同,你的 JavaScript 过渡可以有不同的表现。
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<div id="dynamic-fade-demo" class="demo">
Fade In:
<input type="range" v-model="fadeInDuration" min="0" :max="maxFadeDuration" />
Fade Out:
<input
type="range"
v-model="fadeOutDuration"
min="0"
:max="maxFadeDuration"
/>
<transition
:css="false"
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
>
<p v-if="show">hello</p>
</transition>
<button v-if="stop" @click="stop = false; show = false">
Start animating
</button>
<button v-else @click="stop = true">Stop it!</button>
</div>
const app = Vue.createApp({
data() {
return {
show: true,
fadeInDuration: 1000,
fadeOutDuration: 1000,
maxFadeDuration: 1500,
stop: true
}
},
mounted() {
this.show = false
},
methods: {
beforeEnter(el) {
el.style.opacity = 0
},
enter(el, done) {
var vm = this
Velocity(
el,
{ opacity: 1 },
{
duration: this.fadeInDuration,
complete: function() {
done()
if (!vm.stop) vm.show = false
}
}
)
},
leave(el, done) {
var vm = this
Velocity(
el,
{ opacity: 0 },
{
duration: this.fadeOutDuration,
complete: function() {
done()
vm.show = true
}
}
)
}
}
})
app.mount('#dynamic-fade-demo')
TODO:示例
最后,创建动态过渡的最终方案是组件通过接受 prop 来动态修改之前的过渡。一句老话,唯一的限制是你的想象力。
2. 状态过渡
Vue 的过渡系统提供了非常多简单的方法设置进入、离开和列表的动效。那么对于数据元素本身的动效呢,比如:
- 数字和运算
- 颜色的显示
- SVG 节点的位置
- 元素的大小和其他的 property
这些数据要么本身就以数值形式存储,要么可以转换为数值。有了这些数值后,我们就可以结合 Vue 的响应性和组件系统,使用第三方库(gsap)来实现切换元素的过渡状态。
状态动画与侦听器
通过侦听器我们能监听到任何数值 property 的数值更新。可能听起来很抽象,所以让我们先来看看使用 GreenSock 一个例子:
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.4/gsap.min.js"></script>
<div id="animated-number-demo">
<input v-model.number="number" type="number" step="20" />
<p>{{ animatedNumber }}</p>
</div>
const Demo = {
data() {
return {
number: 0,
tweenedNumber: 0
}
},
computed: {
animatedNumber() {
return this.tweenedNumber.toFixed(0)
}
},
watch: {
number(newValue) {
gsap.to(this.$data, { duration: 0.5, tweenedNumber: newValue })
}
}
}
Vue.createApp(Demo).mount('#animated-number-demo')
更新数字时,输入框下方会对更改设置动画效果。
动态状态过渡
就像 Vue 的过渡组件一样,数据背后状态过渡会实时更新,这对于原型设计十分有用。当你修改一些变量,即使是一个简单的 SVG 多边形也可实现很多难以想象的效果。
把过渡放到组件里
管理太多的状态过渡会迅速增加组件实例的复杂度,幸好很多的动画可以提取到专用的子组件中。我们把之前的示例改写一下:
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.4/gsap.min.js"></script>
<div id="app">
<input v-model.number="firstNumber" type="number" step="20" /> +
<input v-model.number="secondNumber" type="number" step="20" /> = {{ result }}
<p>
<animated-integer :value="firstNumber"></animated-integer> +
<animated-integer :value="secondNumber"></animated-integer> =
<animated-integer :value="result"></animated-integer>
</p>
</div>
const app = Vue.createApp({
data() {
return {
firstNumber: 20,
secondNumber: 40
}
},
computed: {
result() {
return this.firstNumber + this.secondNumber
}
}
})
app.component('animated-integer', {
template: '<span>{{ fullValue }}</span>',
props: {
value: {
type: Number,
required: true
}
},
data() {
return {
tweeningValue: 0
}
},
computed: {
fullValue() {
return Math.floor(this.tweeningValue)
}
},
methods: {
tween(newValue, oldValue) {
gsap.to(this.$data, {
duration: 0.5,
tweeningValue: newValue,
ease: 'sine'
})
}
},
watch: {
value(newValue, oldValue) {
this.tween(newValue, oldValue)
}
},
mounted() {
this.tween(this.value, 0)
}
})
app.mount('#app')
我们能在组件中结合使用这一节讲到各种过渡策略和 Vue 内建的过渡系统。总之,对于完成各种过渡动效几乎没有阻碍。
你可以看到我们如何使用它进行数据可视化、物理效果、角色动画和交互,一切都没有限制。
赋予设计以生命
动画效果可以栩栩如生。不幸的是,设计师创建图标、logo 和吉祥物的时候,他们交付的通常都是图片或静态的 SVG。所以,虽然 GitHub 的章鱼猫、Twitter 的小鸟以及其它许多 logo 类似于生灵,它们看上去实际上并不是活着的。
Vue 可以帮到你。因为 SVG 的本质是数据,我们只需要准备好这些生物兴奋、思考或警戒的样例。然后 Vue 就可以辅助完成这几种状态之间的过渡动画,来制作你的欢迎页面、加载指示和更加带有情感的通知提示。
Sarah Drasner 展示了下面这个根据时间和交互变化发生状态改变的 demo: