Creating Animations Efficiently with

This talk is focused on

the developer ergonomics

of animation.

Good animation requires good ingredients and good execution.

Good tools and techniques make things a lot easier.

GSAP is the go-to library for web animators.

< 150 lines

Start with good resources from the start:

Workflow tips:

  • Know the goal from the start.
  • Make an MVP.
  • Fork something to start it off.

Onwards!

Efficiency tips within tweens/timelines

Use relatives.

  • Relative tweens:  .to() and .from()
  • Relative values:  "+=" and "-="
  • Relative units (vw, vh, %, etc.).

Use relatives.

gsap.fromTo(".box", {x: 0}, { duration: 1, x: 100 });
gsap.to(".box", { duration: 1, x: 100 });
gsap.from(".box", { duration: 1, x: 100 });

Relative tweens

Use relatives.

gsap.fromTo(".box", { x: 50 }, { duration: 1, x: 100 });

Relative values

gsap.to(".box", { duration: 1, x: "+=50" });

Use relative values.

gsap.from(".box", { duration: 1, x: "-=100vw" });
gsap.to(".box", { duration: 1, xPercent: 50 });

Relative units

Use timelines.

const tl = gsap.timeline(); 
tl.to(".box", { duration: 1, x: 100 })
  .to(".box", { duration: 1, backgroundColor: "#f38630" }, "+=0.5") 
  .to(".box", { duration: 1, x: 0, rotation: -360 }, "+=0.5")
gsap.to(".box", { duration: 1, x: 100 });
gsap.to(".box", { duration: 1, backgroundColor: "#f38630", delay: 1.5 }; 
gsap.to(".box", { duration: 1, x: 0, rotation: -360, delay: 3 };

Use defaults.

const tl = gsap.timeline({ defaults: { duration: 1 } }); 
tl.to(".box", { x: 100 })
  .to(".box", { backgroundColor: "#f38630" }, "+=0.5") 
  .to(".box", { x: 0, rotation: -360 }, "+=0.5")

Keyframes + defaults:

gsap.to(".box", { 
  defaults: { duration: 1 },
  keyframes: [
    { x: 100 },
    { backgroundColor: "#f38630", delay: 0.5 }, 
    { x: 0, rotation: -360, delay: 0.5 }
  ]
});

Efficiency tips outside of tweens/timelines

Use control methods

  • Can be used on tweens or timelines.
  • Include .play(), .pause(), .reverse(), .progress(), .seek(), .restart(), .timeScale(), and several others
  • Allow you to create the animations once and reuse them.
  • Allow you to scrub through and go through part of an animation (multiple states).

A click animation

box.addEventListener("click", e => {
  gsap.to(box, { x: 100 });
});
const anim = gsap.to(box, { x: 100, paused: true });
box.addEventListener("click", e => anim.restart() );

How to apply the same animation to multiple elements:

  • One tween, multiple targets — staggers, functional values for variance
  • Multiple animations while staying DRY — loops, functions, and effects

Targeting multiple objects

gsap.to(".box, .circle", { ... });
gsap.to([box, circle], { ... });

Add variance

stagger: {
  amount: 1.5, 
  from: targetElem,
  grid: 'auto'
}

Staggers

stagger: 0.1,

Add variance

const colors = ["#3498db", "#27ae60", "#f1c40f"];

// In your tween
backgroundColor: i => colors[i % 3]

Functional values

y: (i, target, targets) => i % 2 === 1 ? -yMove : yMove

Multiple animations while staying DRY

Loops

elems.forEach(elem => {
  // Scope variables
  let info = container.querySelector(".information"),
      silhouette = container.querySelector(".silhouette .cover"),
      tl = gsap.timeline({ paused: true });
  
  // Animation specific to element(s)
  tl.to(info, { yPercent: 0 })
    .to(silhouette, { opacity: 0 }, 0);
  
  // Add event listeners
  container.addEventListener("mouseenter", () => tl.play() );
  container.addEventListener("mouseleave", () => tl.reverse() );
});

Multiple animations while staying DRY

Loops

elems.forEach(elem => {
  // Scope variables
  let info = container.querySelector(".information"),
      silhouette = container.querySelector(".silhouette .cover"),
      tl = gsap.timeline({ paused: true });
  
  // Animation specific to element(s)
  tl.to(info, { yPercent: 0 })
    .to(silhouette, { opacity: 0 }, 0);
  
  // Add event listeners
  container.addEventListener("mouseenter", () => tl.play() );
  container.addEventListener("mouseleave", () => tl.reverse() );
});

Multiple animations while staying DRY

Functions

function doAnimation() {
  const tl = gsap.timeline();
  tl.to(...);
  tl.to(...);
  // ...as many animations as you’d like!

  // When you’re all done, return the timeline
  return tl;
}

const master = gsap.timeline();
master.add( doAnimation() );
master.add( doAnotherAnimation() );

Multiple animations while staying DRY

Effects

// register the effect with GSAP:
gsap.registerEffect({
    name: "fade",
    defaults: {duration: 2}, //defaults get applied to the "config" object passed to the effect below
    effect: (targets, config) => {
        return gsap.to(targets, {duration: config.duration, opacity:0});
    }
});

// now we can use it like this:
gsap.effects.fade(".box");
// Or override the defaults:
gsap.effects.fade(".box", {duration: 1});
  • .random(), .wrap(), .clamp(), .shuffle(), .mapRange(), etc.
  • Blend eases, FLIP technique, random number based on ease curve, changing transformOrigin without jump, etc.
  • Scroll animation, motion paths, SVG morphing, text splitting, draggable, inertia, physics, dev tools, and more!

Demos

Creating Animations Efficiently with GSAP

By Zach Saucier

Creating Animations Efficiently with GSAP

For JSDays conference on October 14, 2020.

  • 1,832