Table of contents
1.
Introduction
2.
Uses of State Transitions
3.
State transitions with watchers
4.
Dynamic State Transitions
5.
Making state transition components and reusing them
6.
Animating SVGs using State Transitions
7.
FAQs
8.
Key Takeaways
Last Updated: Mar 27, 2024

State Transitions

Author Toohina Barua
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Everything is easy to understand and beautiful when there is movement on the web pages. This movement could be of the components and elements entering, leaving, or changing their form and state in different ways, like, changing shape, opacity, color, etc.

If you keep up with our articles on transitions, you already know that Vue.js provides different ways to animate single and list elements entering and leaving components. Enter and leave transitions and list transitions animate the components. But what if we want to animate our data itself! Here, State transitions come to the rescue! State transitions also help us to bring our stationary SVGs to life! Let us dive right into the topic of State Transitions.

   

Bring SVGs to life with state transitions (Source)

Uses of State Transitions

Using simple Enter and Leave transitions, we can animate the transition of an element appearing to and disappearing from components. If we are working with v-for or a list of elements, we can use list transitions to animate the list elements, entering and leaving the components. However, when we want to add animation and transitions to the data itself, we will be using State transitions. State transitions animate the following:

  • Data that is in the form of numbers
  • The positions and attributes of SVGs
  • The CSS properties of elements

State transitions with watchers

The watch in export default keeps track of or watches the changes in the data mentioned within it. When the data changes, the watcher can perform an event with the new value of the data. In this case, whenever our data changes, we will animate it wherever it is displayed on the Frontend. Let us look at it with a coding example:

index.html

We will be using the GSAP library which will help us animate the elements easily. We will be using it with CDN.

<!DOCTYPE html>
<html lang="en">
 <head>
       <meta charset="UTF-8" />
       <link rel="icon" href="/favicon.ico" />
       <meta name="viewport" content="width=device-width, initial-scale=1.0" />
       <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.4/gsap.min.js"></script> <!-- CDN for gsap library-->
       <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.4/CSSRulePlugin.min.js"></script> <!-- CDN for gsap CSSRulePlugin-->
        <title>State Transitions</title>
 </head>
 <body>
       <div id="app"></div>
       <script type="module" src="/src/main.js"></script>
 </body>
</html>

scr/components/Watchers.vue

Through this code, we will be animating the total cost using the gsap animations. The data has two variables: the number and the tweened number. There is a watcher for the number. So, when we increase or decrease the number, that is, the quantity of the chocolate bars, then the watcher will capture the new value and animate the data using the gsap.to() function. This tweened number will be animated, computed, and displayed in the Frontend. 

<template>
 <div id="animated-number-demo">
       <h4>Price of one chocolate bar: Rs 100</h4>
       <span>Quantity of chocolate bars:   </span>
       <input v-model.number="number" step="1" disabled/>
       <button @click="increaseQty">🔼</button>
       <button @click="decreaseQty">🔽</button>
       <p>Total cost: Rs {{ animatedNumber }}</p>
</div>
</template>
<script>
export default{
     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*100 })
           }
     },
     methods:{
           increaseQty(){
                 this.number++;
           },
           decreaseQty(){
                 if(this.number>0)
                 this.number--;
           }
     }
}
</script>
<style scoped>
input{
     border-radius:5px;
     height:30px;
     font-size:16px;
     margin-right:10px;
}
button{
     margin-right:10px;
     border:1px solid black;
     background-color:dodgerblue;
     padding:5px;
     border-radius:5px;
}
</style>

src/App.vue

Let us import and register the watcher.vue in the App.vue and then run the code.

<template>
     <div>
         <h1>State Transitions</h1>
         <Watchers/>
     </div>
</template>

<script>
import Watchers from "./components/Watchers.vue"
export default{
     components:{
           Watchers,
     }
}
</script>

<style>
#app {
     font-family: Avenir, Helvetica, Arial, sans-serif;
     text-align: center;
     color: #2c3e50;
     margin-top: 60px;
}
</style>

Dynamic State Transitions

We can change the state of elements in real-time and animate them. This is known as Dynamic state transition as we are changing the animation or the state from the frontend itself. Let us have a look at it through a code:

src/components/Dynamic.vue

In this code, we are morphing the polygon into different shapes by changing the points on button click. The points are being watched and whenever there is a change in the points, the transition properties of the polygon are changed. The rotation, x and y used inside the gsap.to() are changed from the frontend using the buttons.

<template>
 <div>
       <svg height="400" width="400">
             <polygon v-bind:points="points" />
       </svg>
       <div>
             <button @click="changeToTriangle">Triangle</button>
             <button @click="changeToSquare">Square</button>
             <button @click="changeToPent">Pentagon</button>
       </div>
 </div>
</template>
<script>
export default{
     data(){
         return {
             points:"100 50,0 0, 50 180",
             finalX:0,
             finalY:0,
             rot:0
         }
     },
     watch:{
         points(newValue) {
               gsap.to("polygon",{rotation: this.rot, x: this.finalX, y:this.finalY,duration: 1})
         }
     },
     methods:{
         changeToTriangle(){
             this.points="100 50,0 0, 50 180"
             this.setState(200,100,27)
         },
         changeToSquare(){
             this.points="0 0,0 100,100 100, 100 0"
             this.setState(300,20,90)
         },
         changeToPent(){
             this.points="10 0,0 100,50 150,100 100, 90 0"
             this.setState(100,150,45)
         },
         setState(x,y,r){
             this.finalX=x
             this.finalY=y
             this.rot=r
         }
     }
}
</script>
<style scoped>
 input{
       margin:10px;
 }
 button{
       margin:10px;
       border:none;
       background:#066052;
       border-radius:5px;
       padding:10px;
       color:white;
 }
 button:hover{
       background:#263D4D;
 }
 polygon{
       fill:teal;
 }
</style>

src/App.vue

Let us replace the Watcher.vue file with the Dynamic.vue in the App.vue.

<template>
     <div>
         <h1>State Transitions</h1>
         <Dynamic/>
     </div>
</template>

<script>
import Dynamic from "./components/Dynamic.vue"
export default{
     components:{
           Dynamic,
     }
}
</script>

Making state transition components and reusing them

We can make transition components and use them wherever and as many times as we want in other components. Given below is an example of organizing transitions into components.

src/components/Compi.vue

Firstly, we will be making the file which will contain the main code for a random score generator. The player with the highest score wins. When the button is clicked, the shake function is called and the random score for both the players are generated and stored. 

<template>
 <div>
       <div><button @click="shake">SHAKE TO GET RANDOM SCORES</button></div>
       <div>
       <span id="classA">
       <span>Player A:</span>
       <Score :pscore="scoreA" />
       </span>
       <span id="classB">
       <span>Player B:</span>
       <Score :pscore="scoreB" />
       </span>
       </div>
       <div><h2>{{winner}}</h2></div>
 </div>
</template>
<script>
import Score from "./Score.vue"
export default{
     components:{
       Score
     },
     data(){
       return{
             winner:"Click the button to find out the winner with the greater score!",
             scoreA:0,
             scoreB:0
       }
     },
     methods:{
       shake(){
             this.scoreA=Math.floor((Math.random() * 100) + 1)
             this.scoreB=Math.floor((Math.random() * 100) + 1)
             if(this.scoreA==this.scoreB){
                 this.winner="Tie!"
             }else if(this.scoreA>this.scoreB){
                 this.winner="Player A is the winner!"
             }else{
                 this.winner="Player B is the winner!"
             }
       }
     }
}
</script>
<style scoped>
 span{
       font-weight:bold;
       margin:10px;
       font-size:24px;
 }
 button{
       background:#1BC9B6;
       color:white;
       padding:10px 30px;
       font-weight:bold;
       margin:30px;
       border:none;
       border-radius:5px;
 }
 button:hover{
       background:#263D4D;
 }
 #classA{
       background:#B29DF3;
       padding:10px 30px;
       margin-top:100px;
       color:white;
       border-radius:5px;
 }
#classB{
       background:#5BBB74;
       padding:10px 30px;
       margin-top:100px;
       color:white;
       border-radius:5px;
 }
</style>

src/components/Score.vue

This is the file which will contain the transition for the data. We are going to use this transition component two times: for player A and for player B. We are going to import this component in the code above.

<template>
 <span>
   {{animatedScore}}
 </span>
</template>
<script>
export default{
 props:["pscore"],
 data(){
   return{
     tweenedScore:0
   }
 },
 computed: {
   animatedScore() {
     return this.tweenedScore.toFixed(0)
   }
 },
 watch:{
   pscore(newValue){
     gsap.to(this.$data, { duration: 0.5, tweenedScore: newValue})
   }
 }
}
</script>

src/App.js

<template>
     <div>
         <h1>State Transitions</h1>
         <Compi/>
     </div>
</template>
<script>
import Compi from "./components/Compi.vue"
export default{
     components:{
           Compi,
     }
}
</script>

Animating SVGs using State Transitions

Scalable vector graphics can be animated using the State Transitions. Adding movement to the SVGs makes them more attractive. Let us make an SVG and add animation to it! You can even download an SVG from the internet and add motion to the different parts. 

src/components/SVG.vue

This is a program for animating a ball made with svg HTML. The start variable is being watched. If it changes to false, the gsap animation will be applied to the ball.

<template>
 <div>
     <svg width="300" height="300">
         <circle cx="150" r="50" stroke="#76D9B4" stroke-width="2" fill="#66F3BF " />
     </svg>
     <div><button @click="drop">Drop the Ball</button></div>
 </div>
</template>
<script>
export default{
   data(){
       return{
           start:true
       }
   },
   watch:{
       start(newValue){
           gsap.to("circle",{y:400,fill:"black",stroke:"black",duration:1,ease: "power1.in",opacity:0})
       }
   },
   methods:{
       drop(){
           this.start=!this.start
       }
   }
  
}
</script>
<style scoped>
 svg{
       background:#D0F9EA;
       border-radius:30px;
 }
 button{
       background:#1BC9B6;
       color:white;
       padding:10px 30px;
       font-weight:bold;
       margin:30px;
       border:none;
       border-radius:5px;
 }
 button:hover{
       background:#263D4D;
 }
</style>

src/App.vue

<template>
     <div>
         <h1>State Transitions</h1>
          <SVG/>
     </div>
</template>
<script>
import SVG from "./components/SVG.vue"
export default{
     components:{
           SVG
     }
}
</script>

FAQs

  1. What are the Javascript libraries that can be used for animation apart from GSAP?
    Some of the JS libraries that can be used for animation are Anime.js, Velocity.js, Popmotion, Three.js, AniJS, mo.js, and many more.
     
  2. How are Dynamic state transitions useful? 
    Dynamic state transitions are useful for prototyping elements and components. You can alter variables in real-time to see what suits you the best.
     
  3. Why do we need state transitions?
    <transition> and <transition-group> tags can be used to animate single and list elements entering and leaving the components with simple CSS. However, when we want to animate the data itself or achieve complex animations, we have to use Javascript that helps us do the state transitions.

Key Takeaways

In this article, we learned about state transitions. We learned how to use watchers to animate the elements. We saw the practicals for dynamic state transitions, animating SVGs, and making transition components. 

But this is not enough; you need something extra to excel in Vue.js truly. If you want to learn more about Vue.js, you can read our articles on Vue.js or take our highly curated Web Development course.

Live masterclass