- CSS animation workflow
- Code technique: Separate styling from logic
- Code technique: Organize sequenced animations
- Code technique: Package your effects
- Design techniques
- Wrapping up
Code technique: Package your effects
One of the most common uses of motion design is fading content in and out of view. This type of animation often consists of a series of individual animation calls that are chained together to deliver a nuanced, multistage effect.
Standard approach
Instead of simply animating the opacity of an element toward 1, you might simultaneously animate its scale property so that the element appears to both fade in and grow into place. Once the element is fully in view, you might choose to animate its border thickness to 1rem as a finishing touch. If this animation were to happen multiple times across a page, and on many different elements, it would make sense to avoid code repetition by turning it into a standalone function. Otherwise, you’d have to repeat this non-expressive code throughout your script.js:
$element .velocity({ opacity: 1, scale: 1 }, { duration: 500, easing: "ease-in-out" }) .velocity({ borderWidth: "1rem" }, { delay: 200, easing: "spring", duration: 400 });
Unlike the sequencing technique discussed in the previous section, the code above consists of multiple animations that all occur on the same element. Chained animations on a singular element constitute an effect. If you were to improve this effect by implementing the first technique in this chapter (turning CSS classes into JavaScript objects), you’d have to go out of your way to uniquely name each argument object for each stage in the overall animation. Not only is it possible that these objects wouldn’t be used by other portions of the animation code due to the uniqueness of this particular sequence, but you’d have to deal with appending integers to each animation call’s respective objects to delineate them from one another. This could get messy, and could neutralize the organizational benefit and brevity of turning CSS classes into JavaScript objects.
Another problem with effects such as the one above is that the code isn’t very self-descriptive—its purpose isn’t immediately clear. Why are there two animation calls instead of one? What is the reasoning behind the choice of properties and options for each of these individual calls? The answers to these questions are irrelevant to the code that triggers the animation, and should consequently be tucked away.
Optimized approach
Velocity’s UI pack lets you register effects that you can subsequently reuse across a site. Once an effect is registered, you can call it by passing its name into Velocity as its first parameter:
// Assume we registered our effect under the name "growIn" $element.velocity("growIn");
That’s a lot more expressive, isn’t it? You quickly understand the code’s purpose: An element will grow into view. The code remains terse and maintainable.
What’s more, a registered effect behaves identically to a standard Velocity call; you can pass in an options object as normal and chain other Velocity calls onto it:
$element // Scroll the element into view .velocity("scroll") // Then trigger the "growIn" effect on it, with the following settings .velocity("growIn", { duration: 1000, delay: 200 })
If the UI pack is loaded onto your page, an effect such as this is registered using the following syntax:
$.Velocity.RegisterEffect(name, { // Default duration value if one isn't passed into the call defaultDuration: duration, // The following Velocity calls occur one after another, with each taking up a predefined percentage of the effect's total duration calls: [ [ propertiesObject, durationPercentage, optionsObject ] , [ propertiesObject, durationPercentage, optionsObject ] ], reset: resetPropertiesObject });
Let’s break down this template step by step:
- The first argument is the name of the effect. If the effect is responsible for bringing an element into view (as in, it fades an element’s opacity from 0 to 1), it’s important to suffix the effect with “In”.
- The second argument is an object that defines the effect’s behavior. The first property in this object is defaultDuration, which lets you specify the duration the full effect should take if one is not passed into the Velocity call that triggers the effect.
- The next property in the object is the calls array, which consists of the Velocity calls that constitute the effect (in the order that they should occur). Each of these array items is an array itself, which consists of the call’s properties object, followed by the optional percentage of the total duration which that call should consume (a decimal value that defaults to 1.00), followed by an optional options object for that specific call. Note that Velocity calls specified within the calls array accept only the easing and delay options.
- Finally, you have the option of passing in a reset object. The reset object is specified using the same syntax as a standard Velocity properties map object, but it is used to enact an immediate value change upon completion of the full effect. This is useful when you’re animating the opacity and scale properties of an element down to 0 (out of view), but want to return the element’s scale property to 1 after the element is hidden so that subsequent effects needn’t worry about the properties beyond opacity they must reset on the element for their calls to properly take effect. In other words, you can leverage the reset properties map to make effects self-contained, such that they leave no clean up duties on the target elements.
In addition to the reset object, another powerful workflow bonus of the UI pack’s effect registration is automatic display property toggling. When an element begins animating into view, you want to ensure its display value is set to a value other than “none” so the element is visible throughout the course of its animation. (Remember, display: none removes an element from the page’s flow.) Conversely, when fading an element out, you often want to ensure its display value is switched to "none" once its opacity hits 0. This way, you remove all traces of the element when you’re done using it.
Using jQuery, display toggling is accomplished by chaining the show() and hide() helper functions onto animations (oftentimes messily buried within nested callbacks). With Velocity’s UI pack, however, this logic is taken care of automatically when you suffix your effect names with “In” and “Out” as appropriate.
Let’s register two UI pack effects—one for the “In” direction and one for the “Out” direction—and call the element “shadowIn” since it consists of fading and scaling an element into view, then expanding its boxShadow property outward:
$.Velocity .RegisterEffect("shadowIn", { defaultDuration: 1000, calls: [ [ { opacity: 1, scale: 1 }, 0.4 ] , [ { boxShadowBlur: 50 }, 0.6 ] ] }) .RegisterEffect("shadowOut", { defaultDuration: 800, calls: [ // We reverse the order to mirror the "In" direction [ { boxShadowBlur: 50 }, 0.2 ], [ { opacity: 0, scale: 0 }, 0.8 ] ] });
If your effect’s name ends with “Out”, Velocity will automatically set the element’s display property to “none” once the animation is complete. Conversely, if your effect’s name ends with “In”, Velocity will automatically set the element’s display property to the default value associated with the element’s tag type (for example, "inline" for anchors, "block" for div and p). If your effect’s name does not contain one of these special suffixes, the UI pack will not perform automatic display setting.
Registering effects not only improves your code, but also makes it highly portable between projects and among fellow developers. When you’ve designed an effect you love, now it’s painless to share the effect’s registration code with others so they can use it too. Pretty neat!