This is the 16th day of my participation in the August Text Challenge.More challenges in August
During development, we wanted to add some kind of transition animation to the display and disappearance of a component, which would add to the user experience
- The React framework does not provide any animation apis, so we need to use a third-party library called React-transition-group to animate React
- Vue provides us with some built-in components and corresponding apis to complete animation, using which we can easily achieve transition animation effects
Transition animations
Vue provides a wrapper component for Transition, and you can add an entry/exit transition to any element or component in the following cases
<template>
<div>
<button @click="isShow = ! isShow">show/hidden</button>
<! -- During the animation execution stage, only the start state (start frame) and end state (end frame) are defined, and then the transition effect between the start frame and end frame is set, so the whole animation can be considered as a transition animation.
<transition name="fade">
<h2 v-if="isShow">message</h2>
</transition>
</div>
</template>
<script>
export default {
name: 'App'.data() {
return {
isShow: true}}}</script>
<style scoped>
/* When the animation is actually executed, vue will automatically add the corresponding animation effect.[Transtiton component set name value]-[style suffix] */
.fade-enter-from..fade-leave-to {
opacity: 0;
}
/* By default, this part of the style is the browser's default Settings, so this part of the style can be omitted without any writing */
/* .fade-leave-from, .fade-enter-to { opacity: 1; } * /
.fade-enter-active..fade-leave-active {
transition: opacity 2s ease;
}
</style>
Copy the code
The class name is named as follows:
- If we use a transition without a name, then all classes are prefixed with v- by default
- If we add a name attribute, for example
<transtion name="foo">
, then all classes will start withfoo-
At the beginning
Transition animation class
When inserting or deleting elements contained in the Transition component, Vue does the following
- Automatically sniff out whether the target element has CSS transitions or animations applied, and if so, add/remove CSS class names when appropriate
- If the Transition component provides JavaScript hook functions, these hook functions will be called at the appropriate time
- If the JavaScript hook is not found and CSS transitions/animations are not detected, DOM insertion and deletion will be performed immediately
Style name | Effective time | The failure time | instructions |
---|---|---|---|
v-enter-from | Takes effect before the element is inserted | The element is removed the next frame after it is inserted | Define the beginning state of the transition |
v-enter-to | The next frame takes effect after the element is inserted (At the same time the v-enter-from was removed) |
Remove after the transition/animation is complete | Define the end state of the transition |
v-enter-active | Applied throughout the transition phase, Takes effect before the element is inserted |
Remove when transition/animation is complete | Defines the state in which the transition takes effect This class can be used to define the process time, delay, and curve functions that enter the transition |
v-leave-from | Takes effect immediately when exit transition is triggered | The next frame is removed | Define the beginning of the exit transition |
v-leave-to | Applied throughout the transition phase of departure, Takes effect immediately when exit transition is triggered |
Remove after the transition/animation is complete | Leaving the end state of transition This class can be used to define exit transition process time, delay and curve functions |
v-leave-active | Removal after transition/animation is complete takes effect on the next frame after departure transition is triggered (At the same time the V-leave-from was deleted) |
Remove after the transition/animation is complete | Defines the state when the exit transition takes effect |
The animate animated
<template>
<div>
<! Div is wrapped to wrap with h2 below -->
<div>
<button @click="isShow = ! isShow">show/hidden</button>
</div>
<transition name="fade">
<! H2 must be set to an inline element or a block element. The default is a block element, so the style is applied to the entire line of h2 and the content is not displayed in the entire line, so the animation effect will be different.
<h2 v-if="isShow">message</h2>
</transition>
</div>
</template>
<script>
export default {
name: 'App'.data() {
return {
isShow: true}}}</script>
<style scoped>
h2 {
display: inline-block;
}
.fade-enter-active {
animation: bounce 1s ease;
}
.fade-leave-active {
animation: bounce 1s ease reverse;
}
/* Define frame animation */
@keyframes bounce {
0% {
transform: scale(0);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1); }}</style>
Copy the code
The animation properties
type
Vue internally listens for transitionEnd or AnimationEnd to know that the transition is complete, depending on the CSS rules applied to the element
If we use only one of them, Vue automatically identifies the type and sets up a listener
But if we use both transitions and animations, in this case one animation may end before the other animation ends
In this case, we can explicitly tell the Vue what type to listen to by setting the type property to animation or Transition
<transition name="fade" type="animation">
<h2 v-if="isShow">message</h2>
</transition>
Copy the code
duration
We can also specify the duration of the transition explicitly, via the Duration property.
Duration can be set to two types of values
- Number type: Sets the transition time for both entry and exit
<transition name="fade" :duration="3000">
<h2 v-if="isShow">message</h2>
</transition>
Copy the code
- Object: Sets the transition time for entering and leaving respectively
<transition name="fade" :duration="{enter: 1000, leave: 2000}">
<h2 v-if="isShow">message</h2>
</transition>
Copy the code
mode
By default, the entry and exit animations occur simultaneously
Problems with switching between two elements
We don’t want to animate both entry and exit at the same time, so we need to set the transition mode:
- In-out: The new element transitions first, and then the current element transitions away
- Out-in: The current element transitions first, and then the new element transitions in
<transition name="fade" mode="out-in">
<h2 v-if="isShow">message</h2>
<h2 v-else>msg</h2>
</transition>
Copy the code
appear
By default, the first rendering is rendered without animation
If we want to animate it, we can add another attribute, “Appear.
<! -- Appear is a Boolean value. The default is false -->
<! So: Appear ="true" -->
<transition name="fade" mode="out-in" appear>
<Home v-if="isShow" />
<About v-else />
</transition>
Copy the code
animate.css
It would be inefficient to write these animations one by one, so in development we might refer to some third-party library such as animation.css
Animate. CSS is a ready-made, cross-platform animation library for our Web projects, useful for emphasis, homepages, swipes, and attention guides
How do I use the Animate library?
- Step 1: You need to install the animate. CSS library
- Step 2: Import styles from the animate. CSS library
- Step 3: Use an Animation or animate class
main.js
import { createApp } from 'vue'
import App from './App.vue'
// Globally introduce animation-related styles
import 'animate.css'
createApp(App).mount('#app')
Copy the code
App.vue
<template>
<div>
<div>
<button @click="isShow = ! isShow">show/hidden</button>
</div>
<transition name="fade" mode="out-in">
<Home v-if="isShow" />
<About v-else />
</transition>
</div>
</template>
<script>
import About from './components/About.vue'
import Home from './components/Home.vue'
export default {
name: 'App'.components: {
Home,
About
},
data() {
return {
isShow: true}}}</script>
<style scoped>
h2 {
display: inline-block;
}
.fade-enter-active {
/* The style name can be retrieved from the animate. CSS website */
animation: bounceInDown 1s ease;
}
.fade-leave-active {
animation: zoomOut 1s ease;
}
</style>
Copy the code
We can also customize the transition class name using the following attributes
- enter-from-class
- enter-active-class
- enter-to-class
- leave-from-class
- leave-active-class
- leave-to-class
They take precedence over ordinary class names, which is useful for Vue’s transition system and other third-party CSS animation libraries such as animation.css. Used in combination, ten points are useful.
<template>
<div>
<div>
<button @click="isShow = ! isShow">show/hidden</button>
</div>
<! Animate__animated is the public style set by animate. CSS -->
<transition
mode="out-in"
enter-active-class="animate__animated animate__fadeIn"
leave-active-class="animate__animated animate__fadeOut"
>
<Home v-if="isShow" />
<About v-else />
</transition>
</div>
</template>
<script>
import About from './components/About.vue'
import Home from './components/Home.vue'
export default {
name: 'App'.components: {
Home,
About
},
data() {
return {
isShow: true}}}</script>
<style scoped>
h2 {
display: inline-block;
}
.fade-enter-active {
animation: bounceInDown 1s ease;
}
.fade-leave-active {
animation: zoomOut 1s ease;
}
</style>
Copy the code
gsap
Before using the animation, let’s take a look at the JavaScript hooks provided by the Transition component. These hooks help us listen to what stage the animation is at
Note:
-
When we use JavaScript to perform transition animations, we need to do the done callback
-
Adding: CSS =”false” will also cause Vue to skip CSS detection,
Vue will no longer set and check CSS styles for animations
In addition to slightly better performance, this avoids the impact of CSS rules during the transition
-
Each hook function takes an argument, EL, that represents the element currently performing the animation
-
For the Enter and leave constructors, there are two arguments:
-
Parameter 1 –> EL is the element that performs the animation
-
Argument 2 –> done is a function that needs to be called to show vue that the corresponding animation life cycle function has finished executing
-
In some cases we want to use JavaScript to achieve some animation effects, in which case we can use the GSAP library.
What is GSAP
- GSAP stands for The GreenSock Animation Platform
- It animates CSS properties, SVG, Canvas, etc., via JavaScript, and is browser-compatible
How should this library be used
- Step 1: You need to install the GSAP library
- Step 2: Import the GSAP library
- Step 3: Use the corresponding API
<template>
<div>
<div>
<button @click="isShow = ! isShow">show/hidden</button>
</div>
<transition
mode="out-in"
@enter="handleEnter"
@leave="handleLeave"
:css="false"
>
<Home v-if="isShow" />
<About v-else />
</transition>
</div>
</template>
<script>
import About from './components/About.vue'
import Home from './components/Home.vue'
import gsap from 'gsap'
export default {
name: 'App'.components: {
Home,
About
},
data() {
return {
isShow: true}},methods: {
// Both enter and leave callbacks take two arguments
// Parameter 1: the element to perform the callback
// Parameter 2: Manually tell vue that the animation is finished, otherwise the corresponding animation will continue to be executed, affecting subsequent animation execution
handleEnter(el, done) {
// What state does the from function represent to restore to the original state
gsap.from(el, {
scale: 1.2.opacity: 0.onComplete: done
})
},
handleLeave(el, done) {
// What state does the initial state need to change to
gsap.to(el, {
scale: 1.2.opacity: 0.onComplete: done
})
}
}
}
</script>
<style scoped>
h2 {
display: inline-block;
}
.fade-enter-active {
animation: bounceInDown 1s ease;
}
.fade-leave-active {
animation: zoomOut 1s ease;
}
</style>
Copy the code
Gsap implements numerical variation
In some projects, we will see animations of rapidly changing numbers that can be easily implemented via GSAP
<template>
<div style="display: inline-block">
<! ToFixed removes the decimal point every time it changes by default, so use toFixed to remove the decimal point and perform the round operation -->
<div>{{ counter.toFixed(0) }}</div>
<button @click="increment">+ 100</button>
</div>
</template>
<script>
import gsap from 'gsap'
export default {
name: 'Home'.data() {
return {
counter: 0}},methods: {
increment() {
// The first argument to the to method can be an object
// It takes 2s to grow the value of 100
// The change is couter + 100
gsap.to(this, { duration: 2.counter: this.counter + 100})}}}</script>
Copy the code
The list of operations
All of the previous transitions were for a single element or component
- Or a single node
- Or one of several nodes is rendered at the same time
What if you want to render a list, and you want to animate adding and deleting data to that list?
We use the
Using
-
By default, it does not render an element wrapper, but you can specify an element and render it with a tag attribute
-
Transition modes (in-out and out-in) are not available because we no longer switch unique elements between each other
-
Internal elements always need to provide a unique key attribute value that identifies which element the animation needs to be added to
-
CSS transitioning classes will be applied to internal elements, not the group/container itself
The basic use
Case study:
- A list of numbers that can be added or removed
- Animate the added and removed numbers as they are added and removed
<template>
<div>
<button @click="add">add</button>
<button @click="remove">remove</button>
<button @click="shuffle">shuffle</button>
<transition-group tag="ul" name="foo">
<li v-for="item in list" :key="item">
{{ item }}
</li>
</transition-group>
</div>
</template>
<script>
import _ from 'lodash'
export default {
name: 'App'.data() {
return {
list: [0.1.2.3.4.5.6.7.8.9].length: 10}},methods: {
add() {
this.list.splice(this.getRandomIndex(), 0.this.length++)
},
remove() {
this.list.splice(this.getRandomIndex(), 1)},shuffle() {
/ / shuffle
this.list = _.shuffle(this.list)
},
getRandomIndex() {
return Math.floor(Math.random() * this.list.length) + 1}}}</script>
<style scoped>
ul.li {
list-style: none;
margin: 0;
padding: 0;
}
ul {
/* ul cannot be set to flex layout because subsequent li elements may be set to relative position when moving. In flex layout, the following elements are automatically filled in and absolute elements are automatically moved to the top of the list (relative to left: 0) this will cause corresponding interference to animation execution */
/* display: flex; * /
margin-top: 10px;
}
li {
margin-right: 10px;
display: inline-block;
}
.foo-enter-from..foo-leave-to {
opacity: 0;
transform: translateY(30px);
}
.foo-enter-active..foo-leave-active {
transition: all 1s ease;
}
.foo-leave-active {
/ * by default, at the time of removing elements, elements can't be left behind moving picture Because is has not been timely remove elements that need to wait for element removed in displacement This animation is pointing in the direction of time, so there will be a flash effect So this time can be set to leave element positioning, let them out of the standard flow, There is no way to block subsequent elements from moving to the left, making way for the */ to move the drawing and remove the animation at the same time
position: absolute;
}
/* By default, new and removed nodes are animated, but other nodes that need to be moved are not animated, so we need to add this style class, which is used to animate before and after inserting and removing nodes. It will apply when the element changes position and just like the name before, we can customize the prefix */ by name
.foo-move {
/* Vue automatically calculates the rest of the attributes */
transition: transform 1s ease;
}
</style>
Copy the code
Staggered transition case
<template>
<div>
<input type="text" v-model="search">
<transition-group
tag="ul"
name="list"
@before-enter="beforeEnter"
@enter="handleEnter"
@leave="handleLeave"
:css="false"
>
<! Set a custom property so that the index value of li can be obtained in js code -->
<li v-for="(user, index) in showUsers" :key="user" :data-index="index">{{ user }}</li>
</transition-group>
</div>
</template>
<script>
import gsap from 'gsap'
export default {
name: 'App'.data() {
return {
search: ' '.users: ['Bruce Lee'.'Jackie Chan'.'Chuck Norris'.'Jet Li'.'Kung Fury']}},computed: {
showUsers() {
return this.users.filter(user= > user.includes(this.search))
}
},
methods: {
beforeEnter(el) {
el.style.height = '0px'
el.style.opacity = '0'
},
handleEnter(el, done) {
// Let's use the gSAP delay property to implement an alternate disappearing animation
gsap.to(el, {
height: '1.5 em'.opacity: 1.delay: el.dataset.index * 0.1.// Get the index, so that the delay value is incremented successively
onComplete: done
})
},
handleLeave(el, done) {
gsap.to(el, {
height: 0.opacity: 0.delay: el.dataset.index * 0.1.onComplete: done
})
}
}
}
</script>
Copy the code