Sunday 7 December 2014

Establishing a Transition Sequence in CSS3

Transitions can be rewarding in CSS3. Gone are the days a developer had to manipulate DOM properties and multiple timing functions in JavaScript. With the right combination of JavaScript and CSS3 - sometimes with only CSS3 - one can work wonders.

Take the following HTML/CSS3/JavaScript code below:

<!DOCTYPE html>
<html>
    <head>
        <title>CSS3 test</title>
        <style type="text/css">
            .bigcircle
            {
                width:200px;
                height:200px;
                border:1px solid #777777;
                background-color:#FFAA00;
                border-radius:150px;
                margin-left:0px;
                -webkit-transition: all 5s;
                transition: all 5s;   
            }

            .newpos
            {
                -webkit-transform: rotate(0deg);
                -moz-transform: rotate(720deg);
                -o-transform: rotate(720deg);
                -ms-transform: rotate(720deg);
                transform: rotate(720deg);
                margin-left:500px;
            }

            .oldpos
            {
                margin-left:0px;
            }

            .smallcircle
            {
                width:20px;
                height:20px;
                margin-top:5px;
                border-radius:9px;
                margin-left:auto;
                margin-right:auto;
                background-color:#FFCC00;          
            }

        </style>

        <script>
            function start_rolling()
            {  
                var pos=(document.getElementById("wheel").className=="bigcircle newpos"?"oldpos":"newpos");
                document.getElementById("wheel").className="bigcircle "+pos;

                if (pos=="oldpos")
                {
                    document.getElementById("btRoll").value="click me to see the ball roll";
                }
                else
                {
                    document.getElementById("btRoll").value="click me to see the ball roll back";
                }
            }
        </script>
    </head>

    <body>
        <input id="btRoll" type="button" value="click me to see the ball roll" onclick="start_rolling();">
        <div id="wheel" class="bigcircle">
            <div class="smallcircle">

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

When the button is clicked, the wheel rolls to the right. Click the button again, the ball rolls to the left. Try it. It works here!



But what happens if you want the wheel to first be invisible, then pop up and roll to the right? And then turn invisible again after rolling back to the left?

Try changing the code below:

<!DOCTYPE html>
<html>
    <head>
        <title>CSS3 test</title>
        <style type="text/css">
            .bigcircle
            {
                width:200px;
                height:200px;
                border:1px solid #777777;
                background-color:#AA7700;
                border-radius:150px;
                margin-left:0px;
                -webkit-transition: all 5s;
                transition: all 5s;
                display:none;
            }

            .newpos
            {
                -webkit-transform: rotate(0deg);
                -moz-transform: rotate(720deg);
                -o-transform: rotate(720deg);
                -ms-transform: rotate(720deg);
                transform: rotate(720deg);
                margin-left:500px;
            }

            .oldpos
            {
                margin-left:0px;
            }

            .smallcircle
            {
                width:20px;
                height:20px;
                margin-top:5px;
                border-radius:9px;
                margin-left:auto;
                margin-right:auto;
                background-color:#FFAA00;          
            }

        </style>

        <script>
            function start_rolling()
            {  
                var pos=(document.getElementById("wheel").className=="bigcircle newpos"?"oldpos":"newpos");
                document.getElementById("wheel").style.display="block";
                document.getElementById("wheel").className="bigcircle "+pos;

                if (pos=="oldpos")
                {
                    document.getElementById("btRoll").value="click me to see the ball roll";
                    document.getElementById("wheel").style.display="none"; 
                }
                else
                {
                    document.getElementById("btRoll").value="click me to see the ball roll back";
                }
            }
        </script>
    </head>

    <body>
        <input id="btRoll" type="button" value="click me to see the ball roll" onclick="start_rolling();">
        <div id="wheel" class="bigcircle">
            <div class="smallcircle">

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

Did it work? Guess not! The ball remained hidden.

Why?

Simply put, not every property can be manipulated by a CSS3 transition. The display property is one of these. While we specified that the change to the margin-left property would take 5 seconds to fully finish, the truth is that both changes to the display and margin-left properties were started simultaneously.

And this confused the processor.

To rectify this, we need to establish a proper sequence for the changes. So we're going to make the following change to the JavaScript.

        <script>
            function start_rolling()
            {  
                var pos=(document.getElementById("wheel").className=="bigcircle newpos"?"oldpos":"newpos");
                document.getElementById("wheel").style.display="block";
                setTimeout(function() {document.getElementById("wheel").className="bigcircle "+pos;},100);

                if (pos=="oldpos")
                {
                    document.getElementById("btRoll").value="click me to see the ball roll";
                    setTimeout(function() {document.getElementById("wheel").style.display="none";},5500);
                }
                else
                {
                    document.getElementById("btRoll").value="click me to see the ball roll back";
                }
            }
        </script>

Magic! Now the ball appears and rolls to the right. And when you click the second time, it rolls back to the left and disappears!

Read more about JavaScript timing functions here: http://www.w3schools.com/js/js_timing.asp

What sorcery is this?!

We introduced a slight time delay, about 100 milliseconds, for the margin-left property to change after the display property change. And when the ball rolled left, we set a time delay (5500 milliseconds) for the ball to disappear. The processor no longer tries to execute changes to a non-transitionable property (display) and a transitionable property (margin-left) at the same time. No more conflict!

Hope you enjoyed reading this. It's how I roll, yo.
T___T

No comments:

Post a Comment