Body of Knowledge

WEB ANIMATION: FINAL

Add Animations to your Website

Part 4 of our Web Animation Series - Final

"A Trilogy in Four Parts" with props to Douglas Adams


This is the fourth and final Article in our short series on Web Animation.

It is aimed primarily, at beginners and so far we have covered some useful Animation examples, in both JavaScript and CSS.

In Part 1 we learned that JavaScript Animation uses the JavaScript scripting language to modify the properties of objects and grasped the process, with a couple of fun examples.

Part 2 saw us blazing a trail into CSS Animation and useful Website gizmos.

And in Part 3, we scythed through our needless apprehensions and created a contemporary video effect to enhance our presentation.

In this, the final chapter, I hope to combine all that we have learned into a couple of intriguing gems.
Today's Article will contain three examples, with the first two being variations on a theme:
  1. The Big Reveal using CSS
  2. The Big Reveal using JavaScript
  3. Another Button Effect, this time using JavaScript

I hope you enjoy the ideas I have selected.

Example Animation 1: CSS Reveal


Ok, so the first example is based on CSS with the following Languages:
  • HTML
  • CSS

HTML creates the objects, CSS the styles and animation.

Example 1: HTML


<div class="css1_container">
   <div class="css1_reveal">
      <a class="css1_anchor" href="JavaScript: return false;">
         <div class="css1_head">The Big Reveal</div>
      </a>
      <div class="css1_CONTENT"></div>
   </div>
</div>


Once again the HTML is quite simple.

Any content to be revealed, is contained in the <div> with the Class css1_CONTENT.

Notes:
  • The Anchor tag is only included to enable the effect to work on touch devices (any clicks or touches are ignored)
  • For the purposes of this example, the content is just a background image but it could literally be anything: text, images or a plethora of HTML

Example 1: CSS


.css1_container {
   width: 700px; max-height: 400px;
   border: 0; margin: 0; padding: 0;
   overflow: hidden;
}
.css1_reveal {
   position: relative;
   left: 300px; top: 0px; width: 100px; height: 22px;
   border: 0; margin: 0; padding: 0;
   transition: all 1s linear;
}
.css1_reveal:hover {
   left: 0px; top: -22px; width: 700px; height: 422px;
}
.css1_anchor {
   text-decoration: none;
   border: 0; margin: 0; padding: 0;
}
.css1_head {
   position: absolute;
   left: 0px; top: 0px; width: 150px; height: 22px;
   background: deepskyblue; color: black;
   font-family: Monaco, "Times New Roman", Arial;
   font-style: normal; font-weight: normal; font-size: 0.9rem;
   text-align: center; vertical-align: middle;
   border: 0; margin: 0; padding: 0;
}
.css1_CONTENT {
   position: absolute;
   left: 0px; top: 22px; width: 700px; height: 400px;
   background-image: url('https://bok.101world.net/assets/images/backs/StxBG113.jpg');
   background-repeat: no-repeat; background-size: 100%, 100%;
   border: 0; margin: 0; padding: 0;
}


The CSS is reasonably straight-forward but lets go through the Classes:
  • css1_container: the background container, it uses overflow: hidden to contain the visible content (only display the bits we want to display)
  • css1_reveal: this contains the revealed content (on hover)
  • css1_reveal:hover: provides the actual reveal by manipulating its position and dimensions
  • css1_anchor: vanilla styles as only required for touch devices
  • css1_head: just a header or tag that starts the reveal
  • css1_CONTENT: the actual revealed content, currently a background image but could be literally anything

Note: The css1_reveal <div> transitions its top (Y position) to -22, hiding the head/tag (css1_head)

Interactive Example: CSS Reveal


Mouse-over or click The Big Reveal to reveal the contents.

Example Animation 2: JavaScript Reveal


The second example uses JavaScript to animate the reveal.

To differentiate it, we'll make it a one-time event. So the content will remain until the page is refreshed.

Example 2: HTML


<div class="css2_container">
   <div id="js2_Reveal" class="css2_reveal" onmouseover="js2_Animate();">
      <a class="css2_anchor" href="JavaScript: return false;">
         <div class="css2_head">Another Reveal</div>
      </a>
      <div class="css2_CONTENT"><a class="css2_reset" href="JavaScript:js2_Reset();">Reset Example</a></div>
   </div>
</div>


The HTML is almost identical to the previous example, except an onmouseover event has been added to replace css1_reveal:hover.

I have also added a Reset Example Button, to allow the reveal to be reset without refreshing the page.

Example 2: CSS


.css2_container {
   width: 700px; max-height: 400px;
   border: 0; margin: 0; padding: 0;
   overflow: hidden;
}
.css2_reveal {
   position: relative;
   left: 300px; top: 0px; width: 150px; height: 22px;
   border: 0; margin: 0; padding: 0;
}
.css2_anchor {
   text-decoration: none;
   border: 0; margin: 0; padding: 0;
}
.css2_head {
   position: absolute;
   left: 0px; top: 0px; width: 150px; height: 22px;
   background: slateblue; color: black;
   font-family: Monaco, "Times New Roman", Arial;
   font-style: normal; font-weight: normal; font-size: 0.9rem;
   text-align: center; vertical-align: middle;
   border: 0; margin: 0; padding: 0;
}
.css2_CONTENT {
   position: absolute;
   left: 0px; top: 22px; width: 700px; height: 400px;
   background-image: url('https://bok.101world.net/assets/images/backs/StxBG113.jpg');
   background-repeat: no-repeat; background-size: 100%, 100%;
   border: 0; margin: 0; padding: 0;
}
.css2_reset {
   color: white;
   text-decoration: underline;
}
.css2_reset:hover {
   background: white; color: black;
}


The CSS is also near identical, except css1_reveal:hover has been removed and a Class had been added for the Reset Example Button.

Example 2: JavaScript


const steps = 50; // Number of animation steps
const mTime = 1000; // Total animation time in milli-seconds

// Cater for Mobile Devices
const deviceWidth = Math.min(window.screen.width, window.innerWidth);
if (deviceWidth > 980) {
   // Standard size
   var startLeft = 300;
   var endWidth = 700;
   var endHeight = 422;
} else {
   // Reduced size for mobile devices
   var startLeft = 200;
   var endWidth = 350;
   var endHeight = 222;
}

const startTop = 0;
const startWidth = 150;
const startHeight = 22;
const endLeft = 0;
const endTop = 0 - startHeight;

const diffX = startLeft;
const diffY = (startHeight - startTop);
const diffW = (endWidth - startWidth);
const diffH = (endHeight - startHeight);

var doReveal = true;

function js2_Animate() {
   var hReveal = document.all('js2_Reveal');
   if (hReveal && doReveal) {
      doReveal = false;
      var s = 1;

      var hIntReveal = setInterval(js2_AnimateReveal, (mTime / steps));

      function js2_AnimateReveal() {
         hReveal.style.left = (startLeft - (diffX * s / steps)) + 'px';
         hReveal.style.top = (startTop - (diffY * s / steps)) + 'px';
         hReveal.style.width = (startWidth + (diffW * s / steps)) + 'px';
         hReveal.style.height = (startHeight + (diffH * s / steps)) + 'px';

         s++;
         if (s > steps)
            clearInterval(hIntReveal);
      }
   }
}

function js2_Reset() {
   var hReveal = document.all('js2_Reveal');
   if (hReveal) {
      var s = 1;

      var hIntReset = setInterval(js2_AnimateReset, (mTime / steps));

      function js2_AnimateReset() {
         hReveal.style.left = (endLeft + (diffX * s / steps)) + 'px';
         hReveal.style.top = (endTop + (diffY * s / steps)) + 'px';
         hReveal.style.width = (endWidth - (diffW * s / steps)) + 'px';
         hReveal.style.height = (endHeight - (diffH * s / steps)) + 'px';

         s++;
         if (s > steps) {
            clearInterval(hIntReset);
            doReveal = true;
         }
      }
   }
}


To allow for mobile devices, the code checks the window size or device-width.

We will be covering Mobile First techniques, in a future Article. But in a nut-shell: if the device-width is greater than 980 pixels, it is treated as a normal Device. Otherwise, it assumes a handheld, and halves the size of the Content.

Other than that, there are no new techniques in the JavaScript. We have covered everything in the previous Articles.

js2_Animate() animates the reveal on mouseover and as a bonus, I added the js2_reset() function, to allow it to be re-run without refreshing the page.

The global variable doReveal controls if/when the reveal can be executed.

This stops the reveal animation from restarting before it completes; this causes shuddering and can occur when multiple mouseover events are delivered.

Interactive Example: JavaScript Reveal


Mouse-over or click Another Reveal to reveal the contents.


As you can see, CSS makes animation much simpler but with JavaScript, you have complete control.

Example Animation 3: JavaScript Button Effect


The final example is a JavaScript Button effect, similar to the CSS version in Part 2 but a little more complex.

Example 3: HTML


<div class="css3_container" onmouseenter="js3_Reset();" onmouseleave="js3_Reset();">
   <a class="css3_anchor" href="JavaScript: return false;">
      <div class="css3_button" onmouseenter="js3_Animate();" onmouseleave="js3_Reset();">Click&nbsp;Here
         <div id="js3_EffectLeft" class="css3_effectLeft">Click</div>
         <div id="js3_EffectRight" class="css3_effectRight">Here</div>
      </div>
   </a>
</div>


The HTML is very similar to the previous Button effect we covered.

It took a bit of work to get some reliabilty with this effect but more of that later.

Using onmouseenter and onmouseleave instead of onmouseover and onmouseout improved the situation immensely.

I also added the two mouse events to the parent container to make sure the Button effect was reliably reset on quick mouse movement.

Example 3: CSS


.css3_container {
   background: white; color: white;
   width: 250px; height: 150px;
   border: 0; margin: 0; padding: 0;
}
.css3_anchor, .css3_anchor:hover {
   border: 0; margin: 0; padding: 0;
   background: inherit; color: inherit;
   text-decoration: none;
}
.css3_button {
   position: relative;
   z-index: 1;
   box-sizing: border-box;
   background-image: url('https://bok.101world.net/assets/images/backs/StxBG102.jpg');
   background-repeat: no-repeat; background-size: 100%, 100%;
   left: 50px; top: 50px; width: 150px; height: 50px;
   font-family: Monaco, "Times New Roman", Arial;
   font-style: normal; font-weight: normal; font-size: 1rem;
   border: 0; margin: 0; padding: 12px 12px 10px 12px;
   overflow: hidden;
}
.css3_effectLeft, .css3_effectRight {
   position: absolute;
   z-index: 2;
   box-sizing: border-box;
   left: -75px; top: 0px; width: 75px; height: 50px;
   background: orangered;
   border: 0; margin: 0; padding: 12px 12px 10px 12px;
}
.css3_effectRight {
   left: 150px;
}


The CSS is relatively simple but let's go through the Classes anyway:
  • css3_container: The parent container
  • css3_anchor: Anchor tag for touch devices only
  • css3_button: The Button object that has two children: the left and right sliders
  • css3_effectLeft: The effect that slides in from the left
  • css3_effectRight: The effect that slides in from the right

Example 3: JavaScript


const noSteps3 = 30; // Number of animation steps
const mTime3 = 300; // Total animation time in milli-seconds
var step3 = 0; // Current step number

const startLeft3 = -75; // Start position of left 'slider'
const startRight3 = 150; // Start position of right 'slider'
const diffX3 = 75; // Total 'slide' amount for each 'slider'

// Effect process/control flags
var doEffect3 = true;
var stopEffect3 = false;

function js3_Animate() {
   // Animate the 'slide-in'
   var hLeft3 = document.all('js3_EffectLeft');
   var hRight3 = document.all('js3_EffectRight');

   if (hLeft3 && hRight3 && doEffect3) {
      doEffect3 = false;
      stopEffect3 = false;

      var hIntEffect3 = setInterval(js3_AnimateEffect, (mTime3 / noSteps3));

      function js3_AnimateEffect() {
         step3++;
         if (step3 > noSteps3 || stopEffect3) {
            clearInterval(hIntEffect3);
         } else {
            hLeft3.style.left = (startLeft3 + (diffX3 * step3 / noSteps3)) + 'px';
            hRight3.style.left = (startRight3 - (diffX3 * step3 / noSteps3)) + 'px';
         }
      }
   }
}

function js3_Reset() {
   // Discard ADDITIONAL 'reset' on some Devices
   if (doEffect3 && step3 == 0)
      return;

   // Set the Effect process/control flags for a 'reset'
   doEffect3 = false;
   stopEffect3 = true;

   // Animate 'slide-out' to reset the 'sliders'
   var hLeft3 = document.all('js3_EffectLeft');
   var hRight3 = document.all('js3_EffectRight');

   if (hLeft3 && hRight3) {
      var hIntReset3 = setInterval(js3_AnimateReset, (mTime3 / noSteps3));

      function js3_AnimateReset() {
         step3--;
         if (step3 < 0) {
            clearInterval(hIntReset3);
            doEffect3 = true;
            step3 = 0;
         } else {
            hLeft3.style.left = (startLeft3 + (diffX3 * step3 / noSteps3)) + 'px';
            hRight3.style.left = (startRight3 - (diffX3 * step3 / noSteps3)) + 'px';
         }
      }
   }
}


Ok, let's start with the simple explantion first.

Firstly, we are using global variables to give the effect object's state. This state allows the sliders to transition from sliding-in to sliding-out smoothly, without restarting the animations.

Secondly, there are two functions that animate the slide-in and slide-out.

The function js3_Animate(), animates the slide-in and is only called on mouseenter into the Button, css3_button.

While, js3_Reset() animates the slide-out and also resets the sliders and relevant JS variables. It is called from:
  • Mouseleave on the Button
  • Mouseenter on the Container
  • Mouseleave on the Container

The last two events add reliability when the mouse pointer is moved quickly through the Button.

Now to the additional reliabilty code I added after a trio of 9.8 difficulty backflips!

You may have noticed the Effect process/control flags I added to get my JavaScript to the reliabilty level of CSS.

doEffect and stopEffect control the contiguity of the animation cycle or process:
  • doEffect: Stops the slide-in from restarting/shuddering before completion; this can occur when multiple mouseenter events are delivered
  • stopEffect: This stops the slide-in when a reset is required; e.g. when a mouseleave event is received

Also, using mouseenter instead of mouseover, as I mentioned previously, greatly improved the reliabilty.

And finally, the slide-in effect was not working on touch devices:
  • I was able to trace that an additional reset was occuring due to a mouseleave event
  • So I added the code at the start of js3_Reset(), to discard the reset when the sliders are in their initial state

The JavaScript version of the Button effect was a lot more difficult than CSS. However, as I mentioned previously, JavaScript does give you complete control and enables modification of objects unrelated to the event.

Interactive Example: JavaScript Button Effect


Mouse-over or click the Button to see the effect.

Conclusion


Well I hope you enjoyed today's examples and the Series in general.

So which do you prefer, CSS or JavaScript?

Although the Series is complete, I will be expanding on it in the future with further Articles aimed at an intermediate level.

Until then, if you have any comments, questions or criticisms please contact us here.
John Berry - Nov 2020
John Berry is a Software Engineer with over 25 years experience in Net Technology and our resident CSS expert.
© 2020 www.101world.net ℗ 2020 www.101world.net

The BoK ... brought to you by 1.01 World Net

The BoK is a Book, a Body of Knowledge,
a collection of Articles, a Knowledge Base
of interesting and useful Information.