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