Friday 20 January 2023

Web Tutorial: Year of the Rabbit Animation

2023 is the Year of the Rabbit in the Chinese Zodiac, and for this upcoming Lunar New Year, this is what the web tutorial will revolve around.

As I did in the Year of the Rat, I am going to turn the numbers into an animal, only this time, I will be using CSS and simple transformations to pull this off.

Here's some code. We first have a div with id container. We want the div to be centered, so set the margin property, and a width and height. Also, let's have a temporary red outline for divs. Since we'll be using some Chinese text as well, we should also specify UTF-8 as the character set.
<!DOCTYPE html>
<html>
    <head>
        <title>Year of the Rabbit</title>
        <meta charset="UTF-8">

        <style>
            div {outline: 1px solid red;}

            .container
            {
                width: 700px;
                height: 500px;
                margin: 10% auto 0 auto;
            }
        </style>

        <script>

        </script>
    </head>

    <body>
        <div class="container">

        </div>
    </body>
</html>


Our objective is to have the numbers 2023, with each number in a different div styled using the number CSS class.
<div class="container">    
  <div class="number">2</div>
  <div class="number">0</div>
  <div class="number">2</div>
  <div class="number">3</div>

</div>


Here's the styling for number. I've set width and height, centering text and making the font large. The color is light grey. Most importantly, the position property is set to absolute and transition is at 1 second.
.container
{
  width: 700px;
  height: 500px;
  margin: 10% auto 0 auto;
}

.number
{
  width: 150px;
  height: 200px;
  text-align: center;
  font-size: 160px;
  font-family: verdana;
  color: rgba(200, 200, 200, 1);
  position: absolute;
  -webkit-transition: all 1s;
  transition: all 1s;
}


And here's how it looks. All cramped together!



For each of these numbers, we need to specify the margin-top and margin-left properties, since their divs are styled using the number CSS class already, and number has the position property set to absolute. Bake this right in the HTML.
<div class="number" style="margin-left:50px;margin-top:100px">2</div>
<div class="number" style="margin-left:200px;margin-top:100px">0</div>
<div class="number" style="margin-left:350px;margin-top:100px">2</div>
<div class="number" style="margin-left:500px;margin-top:100px">3</div>


Now the numbers are nicely spaced out!




We want animation to happen when the user mouses over the container div, and the animation to reverse when the user mouses out. So add this in the div.
<div class="container" onmouseover="doChange()" onmouseout="reverseChange()">


Let's define these functions in the script tag.
<script>
    function doChange()
    {

    }

    function reverseChange()
    {

    }

</script>


In each of these functions, define numbers as an array obtained from running the getElementsByClassName() method. We want to get all elements with that have been styled using the CSS class number, which is all the large numbers making up "2023".
<script>
    function doChange()
    {
        var numbers = document.getElementsByClassName("number");
    }

    function reverseChange()
    {
        var numbers = document.getElementsByClassName("number");
    }
</script>


Now these numbers will animate to form a rabbit! Let's work on the doChange() function. The first element (index 0) will be moved right and slightly down, and scaled to flip horizontally to form the left hind leg.
<script>
    function doChange()
    {
        var numbers = document.getElementsByClassName("number");

        numbers[0].style.transform = "translate(130px, 40px) scale(-1.2, 1.2) ";
    }

    function reverseChange()
    {
        var numbers = document.getElementsByClassName("number");
    }
</script>


The second element (index 1) is the "0", and it will move right, all the way to the center, and enlarged with scaling. This forms the body.
<script>
    function doChange()
    {
        var numbers = document.getElementsByClassName("number");

        numbers[0].style.transform = "translate(130px, 40px) scale(-1.2, 1.2) ";
        numbers[1].style.transform = "translate(70px, 0px) scale(1.5, 1.5)";
    }

    function reverseChange()
    {
        var numbers = document.getElementsByClassName("number");
    }
</script>


The third element (index 2) is the other "2", and will move right and down, and enlarge to form the right hind leg.
<script>
    function doChange()
    {
        var numbers = document.getElementsByClassName("number");

        numbers[0].style.transform = "translate(130px, 40px) scale(-1.2, 1.2) ";
        numbers[1].style.transform = "translate(70px, 0px) scale(1.5, 1.5)";
        numbers[2].style.transform = "translate(20px, 40px) scale(1.2, 1.2)";
    }

    function reverseChange()
    {
        var numbers = document.getElementsByClassName("number");
    }
</script>


The final element is the "3", and we will move it upwards and leftwards, rotate and elongate it to form ears.
<script>
    function doChange()
    {
        var numbers = document.getElementsByClassName("number");

        numbers[0].style.transform = "translate(130px, 40px) scale(-1.2, 1.2) ";
        numbers[1].style.transform = "translate(70px, 0px) scale(1.5, 1.5)";
        numbers[2].style.transform = "translate(20px, 40px) scale(1.2, 1.2)";
        numbers[3].style.transform = "translate(-230px, -180px) rotate(-90deg) scale(3, 0.5)";
    }

    function reverseChange()
    {
        var numbers = document.getElementsByClassName("number");
    }
</script>


Now with reverseChange(), we also fiddle with the transform property. Except that this is far simpler. All translations will be reverted to 0 pixels. Scaling will be at 1, and rotation will be 0 degrees.
<script>
    function doChange()
    {
        var numbers = document.getElementsByClassName("number");

        numbers[0].style.transform = "translate(130px, 40px) scale(-1.2, 1.2) ";
        numbers[1].style.transform = "translate(70px, 0px) scale(1.5, 1.5)";
        numbers[2].style.transform = "translate(20px, 40px) scale(1.2, 1.2)";
        numbers[3].style.transform = "translate(-230px, -180px) rotate(-90deg) scale(3, 0.5)";
    }

    function reverseChange()
    {
        var numbers = document.getElementsByClassName("number");

        numbers[0].style.transform = "translate(0px, 0px) scale(1, 1)";
        numbers[1].style.transform = "translate(0px, 0px) scale(1, 1)";
        numbers[2].style.transform = "translate(0px, 0px) scale(1, 1)";
        numbers[3].style.transform = "translate(0px, 0px) rotate(0deg) scale(1, 1)";

    }
</script>


Neat! Now when you mouse over container, this happens.



Let's add some shading. Add 5 divs in container. Each of them will be styled using the CSS class shading, and have an id.
<div class="container" onmouseover="doChange()" onmouseout="reverseChange()">
    <div class="shading" id="s1"></div>
    <div class="shading" id="s2"></div>
    <div class="shading" id="s3"></div>
    <div class="shading" id="s4"></div>
    <div class="shading" id="s5"></div>  
     
    <div class="number" style="margin-left:50px;margin-top:100px">2</div>
    <div class="number" style="margin-left:200px;margin-top:100px">0</div>
    <div class="number" style="margin-left:350px;margin-top:100px">2</div>
    <div class="number" style="margin-left:500px;margin-top:100px">3</div>
</div>


Here is the styling for shading. It has the following properties...

border-radius - set at 50% to make sure they are round.
background-color - set to a solid black. We will manipulate this later.
position - this is absolute, because we will be positioning the div later via the margin-left and margin-top properties.
transition - this is set at 1 second.
.number
{
    width: 150px;
    height: 200px;
    text-align: center;
    font-size: 160px;
    font-family: verdana;
    color: rgba(200, 200, 200, 1);
    position: absolute;
    -webkit-transition: all 1s;
    transition: all 1s;
}

.shading
{
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 1);
    position: absolute;
    -webkit-transition: all 3s;
    transition: all 3s;
}


Now, we have stylings for each of the divs. We will reference them using the ids. Each of them has different widths, heights and margin-left and margin-top properties.
.shading
{
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 1);
    position: absolute;
    -webkit-transition: all 3s;
    transition: all 3s;
}

#s1
{
    width: 20px;
    height: 150px;
    margin-left: 320px;
    margin-top: -20px;
}

#s2
{
    width: 20px;
    height: 150px;
    margin-left: 350px;
    margin-top: -20px;
}

#s3
{
    width: 80px;
    height: 130px;
    margin-left: 305px;
    margin-top: 150px;
}

#s4
{
    width: 50px;
    height: 100px;
    margin-left: 250px;
    margin-top: 200px;
    transform: rotate(-45deg);
}

#s5
{
    width: 50px;
    height: 100px;
    margin-left: 390px;
    margin-top: 200px;
    transform: rotate(45deg);
}


This is what the divs look like.




Mouse over, and you will see how it aligns with the transformed numbers.




But let's animate the shading as well. Set this to 0% opacity.
.shading
{
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 0);
    position: absolute;
    -webkit-transition: all 3s;
    transition: all 3s;
}


In doChange(), we declare shadings as an array of all elements styled using CSS class shading.
function doChange()
{
    var numbers = document.getElementsByClassName("number");
    var shadings = document.getElementsByClassName("shading");

    numbers[0].style.transform = "translate(130px, 40px) scale(-1.2, 1.2) ";
    numbers[1].style.transform = "translate(70px, 0px) scale(1.5, 1.5)";
    numbers[2].style.transform = "translate(20px, 40px) scale(1.2, 1.2)";
    numbers[3].style.transform = "translate(-230px, -180px) rotate(-90deg) scale(3, 0.5)";
}


Then we use a For loop to iterate through shadings, and set each element's background-color property to 20% opacity.
function doChange()
{
    var numbers = document.getElementsByClassName("number");
    var shadings = document.getElementsByClassName("shading");

    numbers[0].style.transform = "translate(130px, 40px) scale(-1.2, 1.2) ";
    numbers[1].style.transform = "translate(70px, 0px) scale(1.5, 1.5)";
    numbers[2].style.transform = "translate(20px, 40px) scale(1.2, 1.2)";
    numbers[3].style.transform = "translate(-230px, -180px) rotate(-90deg) scale(3, 0.5)";

    for (var i = 0; i < shadings.length; i++)
    {
        shadings[i].style.backgroundColor = "rgba(100, 100, 100, 0.2)";
    }

}


And we do the same in reverseChange(), except we, well, reverse the change!
function doChange()
{
    var numbers = document.getElementsByClassName("number");
    var shadings = document.getElementsByClassName("shading");

    numbers[0].style.transform = "translate(130px, 40px) scale(-1.2, 1.2) ";
    numbers[1].style.transform = "translate(70px, 0px) scale(1.5, 1.5)";
    numbers[2].style.transform = "translate(20px, 40px) scale(1.2, 1.2)";
    numbers[3].style.transform = "translate(-230px, -180px) rotate(-90deg) scale(3, 0.5)";

    for (var i = 0; i < shadings.length; i++)
    {
        shadings[i].style.backgroundColor = "rgba(100, 100, 100, 0.2)";
    }
}

function reverseChange()
{
    var numbers = document.getElementsByClassName("number");
    var shadings = document.getElementsByClassName("shading");

    numbers[0].style.transform = "translate(0px, 0px) scale(1, 1)";
    numbers[1].style.transform = "translate(0px, 0px) scale(1, 1)";
    numbers[2].style.transform = "translate(0px, 0px) scale(1, 1)";
    numbers[3].style.transform = "translate(0px, 0px) rotate(0deg) scale(1, 1)";

    for (var i = 0; i < shadings.length; i++)
    {
        shadings[i].style.backgroundColor = "rgba(100, 100, 100, 0)";
    }

}


So when you mouse over, you can see the change!




When you mouse out, the shadings disappear. But you can still see the red outline! That's useful.




Now let's add some text. This will take the form of two divs within the container div. Both are styled using the text CSS class. The first div contains a message in Chinese text. It is a standard CNY greeting which translates to "Good fortune and prosperity to you, happy Year of the Rabbit!" The second div contains a simple instruction.
<div class="container" onmouseover="doChange()" onmouseout="reverseChange()">
    <div class="shading" id="s1"></div>
    <div class="shading" id="s2"></div>
    <div class="shading" id="s3"></div>
    <div class="shading" id="s4"></div>
    <div class="shading" id="s5"></div>        
    <div class="number" style="margin-left:50px;margin-top:100px">2</div>
    <div class="number" style="margin-left:200px;margin-top:100px">0</div>
    <div class="number" style="margin-left:350px;margin-top:100px">2</div>
    <div class="number" style="margin-left:500px;margin-top:100px">3</div>
    <div class="text">恭喜发财,兔年快乐!</div>
    <div class="text">(Hover to play)</div>

</div>


Here is the styling for text. We have a height and width, center alignment and font specifications. These are cosmetic. The important thing is the position property, which is set to absolute, and the transition property which sets animation speed to 1 second.
#s5
{
    width: 50px;
    height: 100px;
    margin-left: 390px;
    margin-top: 200px;
    transform: rotate(45deg);
}

.text
{
    width: 500px;
    height: 100px;
    text-align: center;
    font-weight: bold;
    font-size: 30px;
    font-family: verdana;
    position: absolute;
    -webkit-transition: all 1s;
    transition: all 1s;
}


Right now, you can see that both divs are overlapping.




Let's fix this stuff. In the HTML itself, we embed margin-left and margin-top properties.
<div class="text" style="margin-left:100px;margin-top:400px;">恭喜发财,兔年快乐!</div>
<div class="text" style="margin-left:100px;margin-top:400px;">(Hover to play)</div>


Now you see both divs are moved lower, and to the middle. But they still overlap!




So let's change the first div to 0% opacity. When you next refresh, it should no longer be visible.
<div class="text" style="margin-left:100px;margin-top:400px;color:rgba(255, 0, 0, 0);">恭喜发财,兔年快乐!</div>
<div class="text" style="margin-left:100px;margin-top:400px;color:rgba(200, 200, 200, 1);">(Hover to play)</div>


In doChange(), declare text as an array of all elements styled using CSS class text.
function doChange()
{
    var numbers = document.getElementsByClassName("number");
    var shadings = document.getElementsByClassName("shading");
    var text = document.getElementsByClassName("text");

    numbers[0].style.transform = "translate(130px, 40px) scale(-1.2, 1.2) ";
    numbers[1].style.transform = "translate(70px, 0px) scale(1.5, 1.5)";
    numbers[2].style.transform = "translate(20px, 40px) scale(1.2, 1.2)";
    numbers[3].style.transform = "translate(-230px, -180px) rotate(-90deg) scale(3, 0.5)";

    for (var i = 0; i < shadings.length; i++)
    {
        shadings[i].style.backgroundColor = "rgba(100, 100, 100, 0.2)";
    }
}


Set the first element (index 0) to 100% opacity, and the second element (index 1) to 0% opacity.
function doChange()
{
    var numbers = document.getElementsByClassName("number");
    var shadings = document.getElementsByClassName("shading");
    var text = document.getElementsByClassName("text");

    text[0].style.color = "rgba(255, 0, 0, 1)";
    text[1].style.color = "rgba(200, 200, 200, 0)";


    numbers[0].style.transform = "translate(130px, 40px) scale(-1.2, 1.2) ";
    numbers[1].style.transform = "translate(70px, 0px) scale(1.5, 1.5)";
    numbers[2].style.transform = "translate(20px, 40px) scale(1.2, 1.2)";
    numbers[3].style.transform = "translate(-230px, -180px) rotate(-90deg) scale(3, 0.5)";

    for (var i = 0; i < shadings.length; i++)
    {
        shadings[i].style.backgroundColor = "rgba(100, 100, 100, 0.2)";
    }
}


In reverseChange(), do the exact opposite!
function reverseChange()
{
    var numbers = document.getElementsByClassName("number");
    var shadings = document.getElementsByClassName("shading");
    var text = document.getElementsByClassName("text");

    text[0].style.color = "rgba(255, 0, 0, 0)";
    text[1].style.color = "rgba(200, 200, 200, 1)";


    numbers[0].style.transform = "translate(0px, 0px) scale(1, 1)";
    numbers[1].style.transform = "translate(0px, 0px) scale(1, 1)";
    numbers[2].style.transform = "translate(0px, 0px) scale(1, 1)";
    numbers[3].style.transform = "translate(0px, 0px) rotate(0deg) scale(1, 1)";

    for (var i = 0; i < shadings.length; i++)
    {
        shadings[i].style.backgroundColor = "rgba(100, 100, 100, 0)";
    }
}


And there you go. Fade in...




...fade out!




This would look much better without the red outline.
div {outline: 0px solid red;}


Yep!




Happy holidays!

I know this doesn't look exactly like a rabbit. But whatevs, dude.

Thank you and have a great 兔 0 兔 3!
T___T

No comments:

Post a Comment