Animations for the Web

Animations for the Web

There is no shortage of animations on the webernetz these days, and libraries to support them. Some web animations are good, and some gratuitous. Recently, I was fortunate to work on a few projects that required animations; therefore, I got a refresher on some concepts. I was also able to extend my knowledge in a few important areas, which I would like to share.

CSS properties to avoid

When animating with CSS, there is one key thing you’ll want to avoid: reflow. When an object changes position or dimensions, it can trigger the browser to process reflow. For example, try to avoid animating positioning using top, bottom, left, right, margins, or even borders. Instead, leverage the transform property and translate functions.

// Avoid reflow: DON'T DO THIS
.animate {
    left: 100px;
    top: 50px;
}

// Avoid reflow: DO THIS
.animate {
    transform: translate(100px 50px);
}

Here is a quick video overview of browser reflows, how they’re caused, and how one might avoid them from Google’s Lindsey Simon.

Animating with CSS or Javascript

It does not have to be binary. Yes, you can animate with CSS only, but typically you might need Javascript to toggle classes on your objects. I’m not certain that there is an absolute difference in performance between using pure CSS vs. Javascript to animate elements. I’ve used Animate.css most often, and recently was able to kick the tires with Velocity.js. The differences were imperceptible to me.

Chrome’s DevTool Animation Inspector

Learn to use it and abuse it. This tool automatically detects animation groups and allows you to scrub through them, and make adjustments on-the-fly. My coworker Eric Fuller first introduced me to the Animation Inspector, and it quickly became my go-to debugging tool.

One main feature is that you can also slow down you animations after being recorded in the Animation Inspector, allowing you to catch easing and timing issues.

screenshot of Chrome's DevTools Animation Inspector

Screenshot of Chrome’s DevTools Animation Inspector

Here is Chrome’s documentation on how to access the Animation Inspector:
https://developers.google.com/web/tools/chrome-devtools/iterate/inspect-styles/animations?hl=en

Accessibility considerations

Introducing movement on the screen may help enhance the experience for some. However, be considerate of users who have vestibular disorders or seizures, and make sure your experience is ultimately inclusive, and not exclusive. WCAG 2.0 offers some guidelines on working with flashing content.

Keyframe syntax

I’ll let the code below speak for itself…

@keyframe name {
    from {}
    to {}
}

// Is the same as...
@keyframe name {
    0% {}
    100% {}
}

// Declarations in a keyframe that are qualified with !important are ignored
@keyframes name {
    from { margin-top: 50px !important; } // ignored
    to   { margin-top: 100px; }
}

Nail your easing

Nothing in nature moves in a straight line. Easing in web animation allows us to control the acceleration and deceleration of elements to give a better sense of natural, personal movement. CSS gives us a few easing keywords: ease, ease-in, ease-out, ease-in-out, steps and linear. However, you’ll quickly find that these can become limiting when working on complex animations, and you may want to generate something more custom, which is where cubic Bézier curves come in. Don’t worry, it may sound mathematical or literally complex, but that is where the following tools come in handy:

Easings.net – allows you to quickly, visually compare and generate the CSS for a slew of easing functions.

CubicBezier.com – allows you to generate unique cubic Bézier easing, as well as compare against traditional easings.

CSS animation easing functions

Leverage hardware acceleration with CSS

Many computers these days support hardware acceleration because they’re equipped with suitable and robust graphics cards. Many browsers leverage this to offload heavy processing to the GPU (Graphics Processing Unit), and certain CSS 3d properties help trigger this, like translateZ() and translate3d(). I recently discovered this when I changed an animation from using scale() to scale3d(). Initially, the animation was quite jittery (aka, jank), but suddenly became smoother with the slight adjustment.

Easing on outta here (See what I did there? 😉 ) 

Making a truly successful and engaging animation is an art form, which takes time and practice. I’m still trying to figure out what all those graphs mean in Chrome’s Timeline. My advice: aim big, but start small.

Get Loopy with Sass

Whether you use Sass/SCSS, Less, or Stylus to compile your CSS, one thing’s for certain; it just makes life much easier. These preprocessors give us access to variables, necessary calculations, and powerful mixins that allow us to take our front-end workflow to the next level.

As time passes the projects I tackle tend to present questions that require unique answers — answers that don’t always need a PHP or programmatic approach; found in the lesser known (or utilized) parts of Sass: control directives. Similar to PHP or Javascript, Sass has if(), @if, @else, @else if, @for, @each, and @while that we can use to conditionally define or loop through styles using booleans or multiple variable checks. Why not save time and energy and make our compiler do the heavy lifting? You could even, in theory, automate the styles for an entire page or global elements. Let’s start off with the basics and end with a bang, then get into something more advanced.

if()

if() is a boolean expression that only returns one of two possible values: true or false, and it works like this.

if(true/false, true, false);

So a use case for this might be:

$boolean: false;

.text-color {
    $color: if($boolean, #000, #fff);
    color: $color;
} // .text-color

Which would output this:

.text-color {
    color: #fff;
} // .text-color

That’s pretty simple. I’ve found this useful when building two containers with same styles, with the exception that their colors are inverted. Using if() checks I can define either white or black depending on div-specific boolean variables.

@if and @else

@if is different than if(), though it is still a boolean, if() is a singular check, while @if evaluates a statement and runs code depending on the outcome. When used in conjunction with@else you can create a loop to allow for multiple checks for true as well as a default should all prior nested statements prove false.

Let’s build on the example above:

$text: #fff;
$boolean: false;

.text-color {

// Internal variables.
$color: if($boolean, #000, #fff);

// Determine background-color.
@if $text == #000 {
    background-color: #fff;
} @else {
    background-color: #000;
}

color: $color;
} // .text-color 

Notice here we’re using ==, but just like Javascript or PHP you have the whole list of operators to choose from including arithmetic (+ - * / %), equality (== !=), logical (and or not), and comparison (> >= < <=) to determine whether or not a statement is true or false.

Bonus: If you want to up your game check out string and color operations.

Which would output this CSS:

.text-color {
    background-color: #000;
    color: #fff;
} // .text-color

Though it does essentially the same thing, we allow for multiple conditionals and multiple possible outcomes with a few lines of code and one variable. A real time saver, and in my opinion, easier to read than a standard if() clause.

We can also do multiple @if checks with @else if, for example:

@if $text == #000 {
    background-color: #fff;
} @else if $text == #fc0 {
    background-color: #69ace5;
} @else if $text == #273143 {
    background-color: #ccc;
} @else {
    background-color: #000;
}

Tip: Remember that numbers, letters, and variables are case sensitive when doing checks.

@for

For those of you familiar with Javascript and jQuery, this might not be unfamiliar, but who knew?

@for can be used two ways looping from x “through” y or from x “to” y. “through” starts and iterates to and including the last item while “to” iterates and does not include that one last iteration.

Here’s a simple @for loop to help us generate helper classes for a grid framework:

@for $i from 1 through 4 {
    $width: 1 / $i;

    .col-#{$i} {
        width: $width;
    } // .col-#{$i}
} // end @for

This would output to:

.col-1 { width: 100%; }
.col-2 { width: 50%; }
.col-3 { width: 33.33%; }
.col-4 { width: 25%; }

Perhaps a more advanced usage might be to define the number of columns before you loop. You could also define mixins inside of the loop. Let’s take bourbon for instance.

$num-columns: 4;

@for $i from 1 through $num-columns {

    .col-#{$i} {
        @include span-columns( $num-columns / $i );
    } // .col-#{$i}
} // end @for

Would output to this:

.col-1 { 
    float: left;
    display: block;
    width: 100%;
}
.col-2 { 
    float: left;
    display: block;
    width: 48.8217%;
    margin-right: 2.35765%;
}
.col-3 { 
    float: left;
    display: block;
    margin-right: 2.35765%;
    width: 31.76157%;
}
.col-4 { 
    float: left;
    display: block;
    margin-right: 2.35765%;
    width: 23.23176%;
}

Note: If I was creating a grid, I might even add conditionals to make .col-1 output @include fill-parent; or even add &:nth-child(x);to remove the margin-right from the last item in a column row.

@each

each() works like you’d expect: @each $variable in <list or map>. You can loop through custom-defined class names to define a color or image (example below), which makes styling similar items a snap.

$type: action, western;

@each $movie in $type {
    .#{$movie}-container {
        background-image: url("assets/images/#{$movie}-bg.jpg");
    } // .#{$movie}-container
} // end @each

Output:

.action-container {
    background-image: url("assets/images/action-bg.jpg");
} // .action-container

.western-container {
    background-image: url("assets/images/western-bg.jpg");
} // .western-container

I would say, super helpful. You can go one step further and define multiple variables within a map.

$type: (action, #fc0), (western, #8a472c);

@each $movie, $color in $type {
    .#{$movie}-container {
        background-image: url("assets/images/#{$movie}-bg.jpg");
        color: $color;
    } // .#{$movie}-container
} // end @each

Which becomes:

.action-container {
    background-image: url("assets/images/action-bg.jpg");
    color: #fc0;
} // .action-container

.western-container {
    background-image: url("assets/images/western-bg.jpg");
    color: #8a472c;
} // .western-container

Using Sass maps is super helpful within loops. Check those out here.

@while

I rarely use @while loops. You can accomplish most everything you might need to achieve with @if or @each directives. That said, they are sometimes useful. Particularly within a @mixin since they run if a condition is met avoiding extra code.

$num: 4;

@while $num > 0 {
    .block-#{$num} {
        content: "#{$num}";
    } // .block-#{$num}
    
    // Reset loop and go again.
    $num: $num - 1;
} // end @while

This outputs:

.block-4 {
    content: "4";
} // .block-4

.block-3 {
    content: "3";
} // .block-3

.block-2 {
    content: "2";
} // .block-2

.block-1 {
    content: "1";
} // .block-1

Advanced Usage

Separately, any of these can be a major help, but you generate more markup to turn a @mixin into something like a function for Sass.

Here’s an example with documentation, which outputs a standard hamburger button.

//-----------------------------------------
// Make the Hamburger Icon
//-----------------------------------------
@mixin generate_hamburger($width, $height, $space, $color, $position, $radius, $speed) {    

  // Start variables.
  $gutter: $height + $space;
  
    // Determine position left right or center.
    @if $position == right {
        float: right;
    } @else if $position == left {
        float: left;
    } @else {
        margin-left: auto;
    margin-right: auto;
    }
    margin-bottom: $height + $space;
    margin-top: $height + $space;
    position: relative;
    text-indent: -9999em;
  
  // All bar sizes.
  &,
  &::before,
  &::after {
    background-color: $color;
    
    // Border radius?
    @if $radius != 0 {
      border-radius: $radius;
    }
    height: $height;
    transition: all $speed ease-in-out;
    width: $width;
  } // &, &::before, &::after
  
  // Top/bottom bars.
  &::before,
  &::after {
    content: "";
    left: 0;
    position: absolute;
  } // &::before, &::after
  
  // Top bar.
  &::before {
    top: -$gutter;
  } // &::before
  
  // Bottom bar.
  &::after {
    bottom: -$gutter;
  } // &::after
}

//-----------------------------------------
// Usage
//-----------------------------------------
.menu-toggle {
    @include generate_hamburger(30px, 5px, 5px, #000, null, 5px, 0.3s);
} // <div class="menu-toggle"></div>

You could take this even further to add a focus state with some animation doing like this. We’ll add a is-active class and some js to help us along.

//-----------------------------------------
// Make the Hamburger Icon
//-----------------------------------------
@mixin generate_hamburger($width, $height, $space, $color, $position, $radius, $speed) {    

  // Start variables.
  $gutter: $height + $space;
  
    // Determine position left right or center.
    @if $position == right {
        float: right;
    } @else if $position == left {
        float: left;
    } @else {
        margin-left: auto;
    margin-right: auto;
    }
    margin-bottom: $height + $space;
    margin-top: $height + $space;
    position: relative;
    text-indent: -9999em;
  
  // All bar sizes.
  &,
  &::before,
  &::after {
    background-color: $color;
    
    // Border radius?
    @if $radius != 0 {
      border-radius: $radius;
    }
    height: $height;
    transition: all $speed ease-in-out;
    width: $width;
  } // &, &::before, &::after
  
  // Top/bottom bars.
  &::before,
  &::after {
    content: "";
    left: 0;
    position: absolute;
  } // &::before, &::after
  
  // Top bar.
  &::before {
    top: -$gutter;
  } // &::before
  
  // Bottom bar.
  &::after {
    bottom: -$gutter;
  } // &::after
  
  // Active state.
  &.is-active{
    @include activate_hamburger(#675aac);
  } // &.is-active
}

// If active.
@mixin activate_hamburger($color) {

  // Top/bottom bars.
  &::before,
  &::after {
    background-color: $color;
  } // &::before, &::after

  // Top bar.
  &::before {
    top: -1px;
    transform: rotate(45deg);
  } // &::before

  // Bottom bar.
  &::after {
    bottom: 1px;
    transform: rotate(-45deg);
  } // &::after
}

//-----------------------------------------
// Usage
//-----------------------------------------
.menu-toggle {
    @include generate_hamburger(30px, 5px, 5px, #000, null, 5px, 0.3s);
} // <div class="menu-toggle"></div>

See the Pen Hamburger Mixins by jomurgel (@jomurgel) on CodePen.

Conclusion

Sometimes it’s the little things. With the goal to optimize load times, increase proficiency, and generate dynamic content easily, it’s a no-brainer that utilizing control directives in Sass is the way to go. At the very least, it can cut your dev time in half on certain tasks. I wholeheartedly recommend taking a deep dive into Sass and the power of if(), @if, @else, @else if, @for, @each, and @while.

We’d love to know how people use loops in new and exciting ways. Let us know in the comments below.

We Have Another New Team Member to Announce!

At WebDevStudios, we’re learning together and growing together. And we’ve added a new face into the mix to grow along with us. His name is Andrew, and you can learn more about him below.

But don’t be jealous of Andrew, because YOU can join our team, too. We’re currently seeking a rock star HR Assistant, an awesome backend developer, and a talented support technician. If any of these job roles sound like you, apply here today.Continue Reading

We Contributed 1,500 Hours to the WordPress Community in 2016

At WebDevStudios (WDS), we love WordPress. We are also very passionate about open-source software in general. For over two years now, WDS has been participating in the Five for the Future (#5ftf) movement. The idea behind #5ftf is that organizations should dedicate 5% of their people to contribute back to the WordPress project, in an effort to help it continue to grow.

In 2016, WDS employees contributed almost 1,500 hours to the WordPress community and projects through our #5ftf involvement. Our team contributed in many different areas including Core development, theme and plugin releases, helping in the support forum, WordCamp and Meetup organization, WordPress tutorials, and more. Our WDS team is so passionate about WordPress that many of our team members contribute to WordPress well beyond our official involvement. It’s absolutely inspiring to see the commitment our team has to WordPress and the entire open-source community.Continue Reading

A New Year and New Learning

If you can already believe it, we’re a month into 2017! The start of each new year is filled with new goals, things we’ll change, and things we’ll continue to improve upon from the previous year – be it personal or professional. With all the hopes and admiration we have for the new year, life quickly takes over and things can get off track. Sometimes this occurs only briefly, but more often, these things don’t even get revisited until next year’s big to-do list.

I could write an entire blog post, or series, on the personal to-do list, but for now, I’ll focus on the professional to-do list, primarily emphasizing the importance of continuing education, while providing you with some great online resources. There is an endless amount of said resources, including video tutorials, that may or may not be WordPress specific, and are either free or low-cost.Continue Reading

Two Added to Our WordPress Crew

Word in the WordPress community spreads fast. And when news that WebDevStudios (WDS) was hiring hit the streets, and by “streets” I mean Twitter; well, it didn’t take too long for us to find the perfect candidates to fill our open positions.

In fact, our latest hires are familiar faces, already acquainted with our executives and many of our current team members. Get to know the new guys featured below. They’re both talented and sometimes even funny, but ultimately, they rock!Continue Reading

Being Productive at Onsite Client Meetings

As a remote worker, who primarily gets to interact with clients via email or Skype conference calls, going onsite to meet with a client is extremely exciting. The face-to-face interaction is one that cannot be underestimated. To have a professional, productive onsite meeting with a client, it takes upfront planning, setting goals, and, the best part, travel. Recently, a team of WDS-ers flew out to Redmond, Washington for an onsite meeting with one of our long-time top clients; and that’s how this story begins…Continue Reading