BigText for animation components

Hits: 0

Article directory

foreword

It’s been a long time since I updated my blog. This period of time is considered to be a small holiday for myself. Do you still remember the animation effects of bigText used in Chinese chess in the last issue?
That’s right, in this issue, I will explain in detail a bigText animation effect that is more complex than that animation effect, and will implement it in a progressive manner in various ways. The final effect is shown in the figure: (Follow-up supplement, you can directly run the final code to see the target effect) (js animation special effects to gif files are difficult to top, is there any other better way)

Realize ideas

Looking closely at the animation, it is not difficult to get the following three important details:

  1. The whole process can be roughly divided into three steps: the text enters, a short pause, and the text disappears.
  2. In the text entry stage, the content of the text list is presented one after another; in the text disappearance stage, the content of the text list disappears synchronously.
  3. The animation effects (text entering and text disappearing) are the same between text lists.

After getting the above message, we might as well implement the effect of a single text first, and then expand it.

keyframes

keyframes core

To achieve animation effects, of course, the simpler the implementation, the better. The first thing to do is to use only CSS, which must be the best.
Obviously, this animation effect is not enough to use css transition transition animation, so let’s go further and try to use css keyframes to achieve it.
Before starting the description, friends who are not familiar with the use of keyframes can check the official documentation: keyframes official documentation
Here I will give some brief descriptions of its use: keyframes realizes the animation effect for this period of time by defining the css style of each keyframe .
Therefore, the key point of using keyframes is also the difficulty in that you need to capture the styles of all different states of the target animation and map them to the keyframes of keyframes one by one.

Realize ideas

Details Observe the animation details, and it is not difficult to capture two important states. (focus on which features change)
Text entry stage:

  1. The opacity of the text is 0, and its position is below its normal position.
  2. The text is fully displayed, 1.25 times larger, and returned to its default position.
  3. The transparency of the text is reduced again, and it enters a pause phase.

Text disappearing stage:

  1. The opacity of the text becomes 0, and its position is above its normal position.

Now that you know all the change states, you only need to pair the change states with keyframes.
Talk is cheap.show you my code:

@keyframes fade {
            0% {
                opacity: 0;
                line-height: 45px;
                transform: scale(1);
            }

            30% {
                opacity: 1;
                line-height: 16px;
                transform: scale(1.25);
            }

            60%,
            80% {
                opacity: 0.6;
                transform: scale(1);
                line-height: 16px;
            }

            100% {
                /* line-height: 4px; */
                opacity: 0;
            }
        }

nitty gritty

Finally, the position change when the text disappears cannot be achieved through line-height. Here I use the margin-top of the parent element to achieve the rising effect, but this means that we also need to write another one for the parent element. Synchronized text disappearing animation:

@keyframes move{

            80% {
                margin-top: 100px;
            }

            100% {
                margin-top: 70px;
            }
        }

expand

At this point, the complete animation process of a single element has been completed using css keyframes, and then try to further extend it to the complete list element.
Full code:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta >
    <title>Document</title>
    <style>
        .box2 {
            width: 100%;
            display: flex;
            justify-content: center;
            flex-wrap: wrap;
            overflow: hidden;
            margin-top: 100px;
            animation: move 1.5s ease-in 1;
        }

        .item {
            width: 100%;
            height: 16px;
            font-family: PingFangSC-Regular;
            text-align: center;
            line-height: 16px;
            color: #383838;
            opacity: 0;
            letter-spacing: 0;
            margin-bottom: 15px;
            animation: fade 1.5s ease-in 1;
        }

        @keyframes fade {
            0% {
                opacity: 0;
                line-height: 45px;
                transform: scale(1);
            }

            30% {
                opacity: 1;
                line-height: 16px;
                transform: scale(1.25);
            }

            60%,
            80% {
                opacity: 0.6;
                transform: scale(1);
                line-height: 16px;
            }

            100% {
                /* line-height: 4px; */
                opacity: 0;
            }
        }

        @keyframes move{

            80% {
                margin-top: 100px;
            }

            100% {
                margin-top: 70px;
            }
        }
    </style>
</head>

< body > 
    < div  class = "box2" > 
        < div  class = "item" > 
            < span > Front-end development </ span > 
        </ div > 
        < div  class = "item" > 
            < span > Back- end development </ span > 
        </ div > 
        < div  class = "item" > 
            < span > Full stack development </span>
        </div>
    </div>
</body>

</html>

Running this code, the animation effect presented is very similar to the target effect, but the animation effect that appears one after another in the target effect is missing, so how should this effect be solved in the end?

Solutions

It can also be solved by using only keyframes. A relatively straightforward solution is to write a specific animation for each list element according to the time difference between successive presentations (the specific implementation of this solution can be tried by yourself). But it’s obviously the same animation effect, just the difference in trigger timing. Can’t reuse the same animation function? The answer is definitely yes, then how to reuse and where does the idea of ​​reuse come from? Next, let’s take a look at the mysteries in the vue transition component.

Transition view

Introduction

Friends who have written animations with Vue should be familiar with the transition component, which is a component that comes with Vue specifically for drawing animations. Its function can actually be similar to the transition of css, that is, transition, but Vue’s transition component defines more transition states, and each transition state can customize its style effect.
There are six transition states in total: v-enter , v-enter-active , v-enter-to , v-leave , v-leave-active and v-leave-to .
For a detailed tutorial on using Vue transition, please refer to the official documentation of Vue: Vue transition documentation

Realize ideas

Next, we try to use Vue transition to implement.
Careful people should not find it difficult to find that the change state in keyframes actually corresponds to the transition state in Vue transition, but the change state here is more than the transition state in transition, so it cannot be completely disassembled.
The css code is as follows:

.fade-enter-to {
            animation: enter-in 1s ease-in 1;
        }

        @keyframes enter-in {
            0% {
                line-height: 45px;
                transform: scale(1);
            }

            40% {
                opacity: 1;
                line-height: 16px;
                transform: scale(1.25);
            }

            60%,
            80% {
                opacity: 0.6;
                transform: scale(1);
                line-height: 16px;
            }

            100% {
                opacity: 0.6;
            }
        }

        .fade-leave-to {
            opacity: 0;
            margin-top: 70px;
            transition: all ease-in 0.4s;
        }

Refactoring

Refactor the code for the above css @keyframes using Vue:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta >
    <script src="./vue-2.5.22.min.js"></script>
    <title>Document</title>
    <style>
        #app {
            overflow: hidden;
        }

        .box2 {
            width: 100%;
            display: flex;
            justify-content: center;
            flex-wrap: wrap;
            overflow: hidden;
            margin-top: 100px;
        }

        .item {
            width: 100%;
            height: 16px;
            font-family: PingFangSC-Regular;
            text-align: center;
            line-height: 16px;
            color: #383838;
            opacity: 1;
            letter-spacing: 0;
            margin-bottom: 15px;
        }

        .fade-enter-to {
            animation: enter-in 1s ease-in 1;
        }

        @keyframes enter-in {
            0% {
                line-height: 45px;
                transform: scale(1);
            }

            40% {
                opacity: 1;
                line-height: 16px;
                transform: scale(1.25);
            }

            60%,
            80% {
                opacity: 0.6;
                transform: scale(1);
                line-height: 16px;
            }

            100% {
                opacity: 0.6;
            }
        }

        .fade-leave-to {
            opacity: 0;
            margin-top: 70px;
            transition: all ease-in 0.4s;
        }
    </style>
</head>

<body>
    <div id="app">
        <template>
            <transition { enter: 1000, leave: 400 }" v-on:before-enter="beforeEnter"
                v-on:after-enter="afterEnter">
                <div class="box2" v-if="show">
                    <div class="item" v-for="item in list">
                        <span>{{item}} </ span > 
                    </ div > 
                </ div > 
            </ transition > 
        </ template > 
    </ div > 
    < script > var app = new Vue({
             el : '#app' ,
             data : {
                 show : false ,
                 list : [ 'front-end development' , 'back-end development' , 'full stack development' ]

            },
            mounted: function () {
                this.show = !this.show;
            },
            methods: {
                beforeEnter: function (el) {
                    el.style.opacity = 0;
                },
                afterEnter(el) {
                    el.style.opacity = 0.6;
                    this.show = !this.show;
                }
            }
        })
    </script>
</body>

</html>

result

Running the above code, we will find that the effect is inconsistent with the target effect. The reason is very simple. The line-height does not take effect for the outer div, so the content in the above transition needs to be split. The outer div uses a separate transition, and the inner div uses a separate transition. The list uses transition-group.
The specific implementation of this scheme can also be tried by yourself. Since it is split in the end, it should also be able to achieve the effects that appear one after another.

subsection

At this point, the warm-up is over. Although the above two implementations have not achieved the final effect (in fact, they can be achieved), they both give us some very important tips. CSS @keyframes reminds us that we need to reuse animation functions, Vue transition reminds us that we need to define appropriate transition states , with these two ideas, then we will enter the revealing link.

custom animation

core

The core of custom animation is the two tips above:

  1. Reusable animation functions
  2. very suitable transition state

Design ideas

First of all, the custom animation must be a class, so what should be needed in the class? A common class has properties and methods, so the custom animation class must also have properties and methods. What are its properties, analogous to Vue transition, its properties should come from external configuration parameters, then what should its methods be, yes, the transition state function and the animation start function.
Talk is cheap,show you my code:

class Transition {
            constructor(dom, className, timeout, isDestory) {
                this.dom = dom;
                this.className = className;
                this.timeout = timeout;
                this.isDestory = isDestory;
                this.preName = '';
            }

            startEnter() {
                this.enter();
                setTimeout(() => {
                    this.enterActive();
                    setTimeout(() => {
                        this.enterDone();
                    }, this.timeout);
                }, this.timeout);
            }

            startExit() {
                this.exit();
                setTimeout(() => {
                    this.destory();
                }, this.timeout);
            }


            destory() {
                if (this.isDestory) {
                    this.dom.parentElement.removeChild(this.dom);
                }
            }

            enter() {
                this.preName = this.className + '-enter';
                this.dom.classList.add(this.preName);
            }

            enterActive() {
                this.dom.classList.remove(this.preName);
                this.preName = this.className + '-enter-active';
                this.dom.classList.add(this.preName);
            }

            enterDone() {
                this.dom.classList.remove(this.preName);
                this.preName = this.className + '-enter-done';
                this.dom.classList.add(this.preName);
            }

            exit() {
                if (this.preName !== '') {
                    this.dom.classList.remove(this.preName);
                }
                this.preName = this.className + '-exit';
                this.dom.classList.add(this.preName);
            }
        }

This Transition class is tailored according to the target animation effect, among which enter, enterActive, enterDone, exit and destroy belong to the transition state function, and startEnter and startExit belong to the animation start function.
Maybe here, you may not feel its power, so let’s start using it to transform the code.

Refactored list animation

code show as below:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta >
    <title>Document</title>
    <style>
        .box2 {
            width: 100%;
            display: flex;
            justify-content: center;
            flex-wrap: wrap;
            overflow: hidden;
            margin-top: 100px;
        }

        .item {
            width: 100%;
            height: 16px;
            font-family: PingFangSC-Regular;
            text-align: center;
            line-height: 16px;
            color: #383838;
            opacity: 0;
            letter-spacing: 0;
            margin-bottom: 15px;
        }

        .fade-enter {
            opacity: 0;
            line-height: 45px;
            transform: scale(1);
        }

        .fade-enter-active {
            opacity: 1;
            line-height: 16px;
            transform: scale(1.25);
            transition: all ease-in 0.4s;
        }

        .fade-enter-done {
            opacity: 0.6;
            transition: all linear 0.4s;
        }

        .fade-exit {
            opacity: 0;
            transition: all ease-in 0.4s;
        }

        .move-exit {
            margin-top: 70px;
            transition: all ease-in 0.4s;
        }
    </style>
</head>

< body > 
    < div  class = "box2" > 
        < div  class = "item" > 
            < span > Front-end development </ span > 
        </ div > 
        < div  class = "item" > 
            < span > Back- end development </ span > 
        </ div > 
        < div  class = "item" > 
            < span > Full stack development </span>
        </div>
    </div>
    <script>
        class Transition {
            // ...
        }
        const doms = document.getElementsByClassName('item');
        const dom = document.getElementsByClassName('box2')[0];
        const tl0 = new Transition(doms[0], 'fade', 400, true);
        const tl1 = new Transition(doms[1], 'fade', 400, true);
        const tl2 = new Transition(doms[2], 'fade', 400, true);
        const tl = new Transition(dom, 'move', 400, true);
        tl0.startEnter();
        tl1.startEnter();
        tl2.startEnter();
        setTimeout(() => {
            tl0.startExit();
            tl1.startExit();
            tl2.startExit();
            tl.startExit();
        }, 1300);
    </script>
</body>

</html>

Running this code, it is not difficult to find that the effect is exactly the same as that of the first keyframes scheme. Careful people should find that we also reuse the startExit animation startup function here, which improves the reusability of the code compared to the keyframes scheme.
At this point, there is still one last step, adding the effects that are presented one after another, and the moment to witness the miracle has come.

final solution

It is precisely because we disassembled the text entry stage and the text disappearance stage into two animation startup functions, so it is not easy to start one after another.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta >
    <title>Document</title>
    <style>
        .box2 {
            width: 100%;
            display: flex;
            justify-content: center;
            flex-wrap: wrap;
            overflow: hidden;
            margin-top: 100px;
        }

        .item {
            width: 100%;
            height: 16px;
            font-family: PingFangSC-Regular;
            text-align: center;
            line-height: 16px;
            color: #383838;
            opacity: 0;
            letter-spacing: 0;
            margin-bottom: 15px;
        }

        .fade-enter {
            opacity: 0;
            line-height: 45px;
            transform: scale(1);
        }

        .fade-enter-active {
            opacity: 1;
            line-height: 16px;
            transform: scale(1.25);
            transition: all ease-in 0.4s;
        }

        .fade-enter-done {
            opacity: 0.6;
            transition: all linear 0.4s;
        }

        .fade-exit {
            opacity: 0;
            transition: all ease-in 0.4s;
        }

        .move-exit {
            margin-top: 70px;
            transition: all ease-in 0.4s;
        }
    </style>
</head>

< body > 
    < div  class = "box2" > 
        < div  class = "item" > 
            < span > Front-end development </ span > 
        </ div > 
        < div  class = "item" > 
            < span > Back- end development </ span > 
        </ div > 
        < div  class = "item" > 
            < span > Full stack development </span>
        </div>
    </div>
    <script>
        class Transition {
            constructor(dom, className, timeout, isDestory) {
                this.dom = dom;
                this.className = className;
                this.timeout = timeout;
                this.isDestory = isDestory;
                this.preName = '';
            }

            startEnter() {
                this.enter();
                setTimeout(() => {
                    this.enterActive();
                    setTimeout(() => {
                        this.enterDone();
                    }, this.timeout);
                }, this.timeout);
            }

            startExit() {
                this.exit();
                setTimeout(() => {
                    this.destory();
                }, this.timeout);
            }


            destory() {
                if (this.isDestory) {
                    this.dom.parentElement.removeChild(this.dom);
                }
            }

            enter() {
                this.preName = this.className + '-enter';
                this.dom.classList.add(this.preName);
            }

            enterActive() {
                this.dom.classList.remove(this.preName);
                this.preName = this.className + '-enter-active';
                this.dom.classList.add(this.preName);
            }

            enterDone() {
                this.dom.classList.remove(this.preName);
                this.preName = this.className + '-enter-done';
                this.dom.classList.add(this.preName);
            }

            exit() {
                if (this.preName !== '') {
                    this.dom.classList.remove(this.preName);
                }
                this.preName = this.className + '-exit';
                this.dom.classList.add(this.preName);
            }
        }
        const tl0 = new Transition(doms[0], 'fade', 400, true);
        const tl1 = new Transition(doms[1], 'fade', 400, true);
        const tl2 = new Transition(doms[2], 'fade', 400, true);
        const tl = new Transition(dom, 'move', 400, true);
        tl0.startEnter();
        setTimeout(() => {
            tl1.startEnter();
            setTimeout(() => {
                tl2.startEnter();
                setTimeout(() => {
                    tl0.startExit();
                    tl1.startExit();
                    tl2.startExit();
                    tl.startExit();
                }, 1300);
            }, 400);
        }, 400);
    </script>
</body>

</html>

Running this code will render exactly the same as the target animation.
In fact, the writing of custom animation is similar to recreating the wheel of a Vue transition, but it is a simplified version, but it is more practical.
Although this method is powerful and versatile, if the target effect can be simply achieved by using the existing wheel, then of course there is no need to spend a lot of time and energy to recreate an existing wheel that is exactly the same.

Epilogue

At this point, this article is coming to an end. This article uses three ways to achieve an animation effect so that everyone can feel the applicable scene of each method, so that everyone can make more accurate judgments and trade-offs when writing animations. If you have any questions about this article, please leave a message in the comment area or send me a private message, and I will respond positively. Sending you a thousand miles will have to say goodbye, and I will meet you next time.

You may also like...

Leave a Reply

Your email address will not be published.