CSS 105 - Basic animation

"You're a DOM element. Your spouse is considering leaving you because you're a bit of a square. The padding underneath your feet feels thin these days, and you're just squeezing above the fold by a small margin. You're a nobody. You aren't making h1 lines. Suddenly someone gives you a "transition: top 0.2s ease-in". Your transition to the top is going to be a smooth and quick one. Everyone else can eat it, bitches. You've got animation on your side now."
I need to stop spending time writing this stuff

Animation is an easy way to make an element stand out in an otherwise static page. Just as important as knowing *how* to do animation is understanding *when* to use animation, so before we jump into the how-to let's review why we should use it.

  1. Animation guidelines

    • Animate only the most important thing

      Remember that any movement on the page will catch the eyes. Drawing user focus should be used like a tool, not just tossed around willy nill. Only animate one thing at a time, don't have every element flying around at once. Also keep in mind that the most important thing on the page changes with what a user is doing - if a user clicks something, that's the most important thing.

      As a rule of thumb, always animate only the most important data on the page, or the part the user is currently interacting with.
    • If you have to animate more than one thing, cascade the animations

      As mentioned before, animations draw the user focus. If you have to animate more than one thing, trigger them one after another so the user focuses on a single piece at a time.

    • Use eminance

      If an animation is tied to a location, animate from there. Let a user know where something is coming from. Look at the example below - if we animate where something is, we can return to that state easier. If the animation has no direction, we don't know which one of the three buttons holds the important popup.

      Without eminance
      With eminance
    • If no eminance, animate upwards

      Movement upwards is part of our design guidelines. We want pages to feel optimistic and light, so we move up as our default direction.

  2. The transition property

    A transition allows you to define how an element moves when its css values are changed. The syntax of the delcaration comes in four parts:
    .element
      transition: PROPERTY DURATION EASING DELAY

    Property and duration are the only two that are required, the others are optional. Let's ignore the last two for now and look at some examples. Click the red boxes to see the transition in action.

    The property is any css property. Note that the first example uses left and tweens between left values, whereas the second uses background-color.
    #transitionBox
      position: absolute
      left: 0px
      transition: left 1s
    #transitionBox.left
      left: 100px
    #transitionBox2
      background-color: $ak-color-R300
      transition: background-color 1s
    #transitionBox2.blue
      background-color: $ak-color-B300
    You can also use "all" as a wildcard
    #transitionBox3
      background-color: $ak-color-R300
      width: 100px
      transition: all 1s
    #transitionBox3.wideBlue
      background-color: $ak-color-B300
      width: 200px
    You can even chain styles with different timings
    #transitionBox4
      width: 100px
      left: 0px
      transition: left 1s, width 2s
    #transitionBox4.wideLeft
      left: 100px
      width: 200px
  3. Easing

    The third argument is the easing. Note that the box in blue moves fluidly across the page, whereas the box in red moves at a constant velocity. The difference is in the easing function.
    The red box uses a linear easing function, meaning it maintains its velocity during the entire transition. The blue box uses an ease-in-out, meaning that it speeds up toward the halfway point, then slows down after. This more closely mimicks objects in the real world that have weight and acceleration tied to them. If no easing is defined, it defaults to ease-in-out.
    There are several easing functions you can use. Knowing which to use where takes practice, but in general try to emulate how an object would react in the real world. The built in functions are the following:
    Keyword cubic-bezier
    linear cubic-bezier(0, 0, 1, 1)
    ease cubic-bezier(0.25, 0.1, 0.25, 1)
    ease-in cubic-bezier(0.42, 0, 1, 1)
    ease-in-out cubic-bezier(0.42, 0, 0.58, 1)
    ease-out cubic-bezier(0, 0, 0.58, 1)
    Note the cubic-bezier that's after the keyword. That is the actual curve that the keyword represents. The keywords are simply shorthand for the actual values, similar to how "red" is shorthand for #f00. If you want you can even use custom curves in your code:
    .element
      transition: left 0.3s cubic-bezier(0.17, 0.67, 0.83, 0.67)
    To play around with cubic bezier curves and transitions, try cubic-bezier.com

    The Atlassian ease

    In adg3, we've defined two curves:
    $ak-ease-out = cubic-bezier(0.15, 1, 0.3, 1)
    $ak-ease = cubic-bezier(0.2, 0, 0, 1)
    
    .element
      transition: all 0.3s $ak-ease
    
    This curve gives a nice balance of quickness and impact on the page. Use $ak-ease-out whenver something is moving in reaction to a direct interaction, i.e. you click a box and the box itself moves. Use $ak-ease whenever something is moving in reaction to a separate event, i.e. you click a button and the sidebar pops out. $ak-ease-out is more explosive, making items that we click feel snappier when they react to that click. $ak-ease is more smooth and graceful, making elements that we aren't clicking feel less jerky.

    Timing

    For timing, keep your transitions between 0.1s and 0.5s, depending on the size of the element and the distance it moves. The larger the element, the slower it should move because it has more "weight". The further it moves, the longer the transition time should be.

    Pop Quiz

    What timing should the following motions have, assuming we're moving each element into the bordered square beside it?

    transition: all 0.2s $ak-ease-out
    transition: all 0.1s $ak-ease-out
    transition: all 0.25s $ak-ease-out
    
    Timings are very subjective. Use your best judgement and try to keep the feel of it bold but still defined by the physics of the element you're animating.
    Animation is a design element to a page. Like any design, don't overdo it. Keep it subtle and give it importance on a page.
  4. Delay

    The final argument is delay. This is especially useful for creating a cascading effect:
    #cascadeExample
      .box
        position: relative
        opacity: 0
        top: 60px
      &.active
        .box
          top: 0
          opacity: 1
          transition: top 0.3s $ak-ease, opacity 0.15s $ak-ease
        .box:nth-child(2)
          transition: top 0.3s $ak-ease 0.05s, opacity 0.15s $ak-ease 0.05s
        .box:nth-child(3)
          transition: top 0.3s $ak-ease 0.1s, opacity 0.15s $ak-ease 0.1s
        .box:nth-child(4)
          transition: top 0.3s $ak-ease 0.15s, opacity 0.15s $ak-ease 0.15s
    Another thing delay is useful for is delaying the loading of the most important thing on the page. If you remember from the animation guidelines earlier, animating the foremost element draws attention to it. Delaying that animation will give the page time to settle before your animation triggers, drawing more attention to it as you don't have any reflows going on at that time. Paradoxically sometimes adding a delay can increase a users' perceived performance of a page.
  5. Exercises

    1. Create a box that gets bigger when you click it once.

      • Create a div. Give it a width, a height, a background color, and a transition.
      • Select the div in javascript and add a click event listener to it. Pass it a function that toggles a class of 'active' on the element.
      • In your stylus, add a larger width and height to the element if it has an active class

      Example:

      Answer:

      #growingBox
      #growingBox
        width: 100px
        height: 100px
        // remember to require akcolor.styl in your stylesheet.
        background-color: $ak-color-R300
        cursor: pointer
        transition: all 0.2s $ak-ease
        &.active
          width: 200px
          height: 200px
      document.getElementById('growingBox').addEventListener('click', function(){
        document.getElementById('growingBox').classList.toggle('active')
      })
      
    2. Create a modal popup

      • Download this image and place it in an images/ folder in your project. Add it as a background image to a div. Set the opacity to 0.
      • Create a button with the text "open popup"
      • When clicked, animate the popup so it moves up and fades in.
      • Add a listener to a popup that closes it when clicked, if it's open.

      Example:

      Hint:

      Use this stylus mixin. It will automatically generate the width/height/background of your image element. Creating a div from an image is a very common thing in prototyping, so this image() function will be quite useful:
      image(path)
        size = image-size('images/' + path)
        background-size: operate('/', size[0], 2)
        background-image: url('images/' + path)
        width: operate('/', size[0], 2)
        height: operate('/', size[1], 2)
        background-repeat: no-repeat
      
      .element
        image('modalSmall.png')
      
      .element {
        background-size: 400px;
        background-image: url("images/modalSmall.png");
        width: 400px;
        height: 488px;
        background-repeat: no-repeat;
      }
      

      Answer:

      #popup
      input#openPopup(type='button' value='open popup')
      #popup
        position: fixed
        box-shadow: 0px 2px 10px 3px rgba(0,0,0,0.1)
        border-radius: 4px
        cursor: pointer
        pointer-events: none
        top: 200px
        opacity: 0
        z-index: 100
        transition: all 0.3s $ak-ease
        image('modalSmall.png')
        &.active
          top: 160px
          opacity: 1
          pointer-events: all
      document.getElementById('openPopup').addEventListener('click', function(){
        document.getElementById('popup').classList.add('active')
      })
      document.getElementById('popup').addEventListener('click', function(){
        document.getElementById('popup').classList.remove('active')
      })