2026年5月7日
By: Chase

Learn GSAP-1

前言 Preface

最近刷职位, 发现不少需要酷炫吊炸天landing page需求的, 此blog作为学习笔记, 学习一下当今天下第一的GSAP.

Recently, while searching job postings, I noticed that many positions require creating luxury and cool landing pages. This blog serves as my study notes as I learn the world's top animation library, GSAP.

这里直接引入gsap@3.15, 所有demo都可以编辑预览.

We import the gsap@3.15 here, all the demos running at this page could edit to trigger animate.


    // import the library
    gsap.version
  

学习 Learning

基础 Base

这章基本可以跳过, 基本就是CSS transform的GSAP API写法, 不过确实很简洁.

This chapter is easy to learn since I am familiar with the related CSS attributes. But GSAP is with a simple way to write.

The official demo document.

To and From

我已经直接设置了box居中作为初始位置.

I have set the box in center of the row with margin:auto.

const toFunction = () => {
  gsap.set("#box1", { x: 0 })
  void gsap.to("#box1", { x: 100 })
}
document.querySelector("#box1-to").onclick = toFunction
void toFunction()

const fromFunction = () => {
  gsap.set("#box1", { x: 0 })
  void gsap.from("#box1", { x: -100 })
}
document.querySelector("#box1-from").onclick = fromFunction

const fromTo = () => {
  gsap.set("#box1", { x: 0 })
  void gsap.fromTo("#box1", { x: -100 }, { x: 100})
}
document.querySelector("#box1-from-to").onclick = fromTo

Other params

const testOtherParams = () => {
  gsap.killTweensOf("#box2")
  gsap.set("#box2", { x: 0, scale: 1, rotation: 0, opacity: 1 })
  void gsap.to("#box2", {
    x: 100,	                           // transform: translateX(100px)
    // y: 100,	                       // transform: translateY(100px)
    // xPercent: 50,	                 // transform: translateX(50%)
    // yPercent: 50,	                 // transform: translateY(50%)
    // scale: 2,	                     // transform: scale(2)
    // scaleX: 2,	                     // transform: scaleX(2)
    // scaleY: 2,	                     // transform: scaleY(2)
    // rotation: 90,	                 // transform: rotate(90deg)
    // skew: 30,	                     // transform: skew(30deg)
    // skewX: 30,	                     // transform: skewX(30deg)
    // skewY: "1.23rad",	             // transform: skewY(1.23rad)
    // transformOrigin: "center 40%",  // transform-origin: center 40%
    // opacity: 0,	                   // adjust the elements opacity
    // autoAlpha: 0,	                 // shorthand for opacity & visibility
    // duration: 1,	                   // animation-duration: 1s
    // repeat: -1,	                   // animation-iteration-count: infinite
    // repeat: 2,	                     // animation-iteration-count: 3
    // delay: 2,	                     // animation-delay: 2s
    // yoyo: true,	                   // animation-direction: alternate
  })
}
void testOtherParams();
document.querySelector("#box2-button").onclick = testOtherParams

Modify the SVG

const testSVG = () => {
  gsap.set("#rect", {
    x: 0,
    attr: {
      fill: "#28a92b"
    }
  })
  void gsap.to("#rect", {
    x: 20,
    duration: 2,
    attr: {
      fill: "#9d95ff",
    },
  })
}
void testSVG();
document.querySelector("#svg-button").onclick = testSVG

Easing

这一章略了, 官方文档配的坐标系看起来很直观.

We pass the demo for this,

https://gsap.com/resources/getting-started/Easing

Stagger

GSAP的强大在这章初步体现, 交错动画不需要人工调度.

It begins showing the power since this chapter.

const staggers = () => {
  gsap.killTweensOf(".stagger-box")
  void gsap.to(".stagger-box", {
    y: 50,
    // stagger: 0.25,
    repeat: -1,
    stagger: {
      amount: 1,
      // each: 0.25,
      // from: 'center',
      // grid: 'auto',
      // axis: 'x',
      ease: 'power2.inOut',
    }
  })
}
document.querySelector("#staggers-button").onclick = staggers
void staggers()

Timeline

上一章节的stagger是预制版的timeline实现, GSAP以一条总的globalTimeline为基准管理调度所有动画.

GSAP manages all the animations with one globalTimeline, and the stagger in the previous chapter could be considered as pre-configured implementation of a timeline.

const timeline = () => {
  // reset
  gsap.set(["#timeline-box1", "#timeline-box2", "#timeline-box3"], { x: 0 })

  // create a timeline
  let tl = gsap.timeline()

  // add the tweens to the timeline - Note we're using tl.to not gsap.to
  tl.to("#timeline-box1", { x: 300, duration: 2 });
  tl.to("#timeline-box2", { x: 300, duration: 1, delay: 1 });
  tl.to("#timeline-box3", { x: 300, duration: 1 });
}
document.querySelector("#timeline-button").onclick = timeline
void timeline()

他还有一个position参数更方便的管理动画, 顺序基于上一个动画的结束时间.

It also support a param we could call it position, the * below.

tl.to("#timeline-box1", { x: 300, duration: 2 }, *);
写法含义
数字(如 1、0.5)绝对时间:从 timeline 开头算起的秒数。
"+=0.5"相对于上一段动画结束,再延迟 0.5 秒后开始(基于「上一段结束」的相对时间)。
"-=0.5"比上一段动画结束提前 0.5 秒开始(与上一段动画重叠)。
"<"与上一条子动画的开始时刻对齐(同一时刻开始)。
"<0.5"在上一条动画开始后 0.5 秒再开始。
">"与上一条子动画的结束时刻对齐(上一条刚结束时开始)。
">0.5"在上一条动画结束后 0.5 秒再开始。
SyntaxMeaning
Number (e.g. 1, 0.5)Absolute time in seconds from the start of the timeline.
"+=0.5"Start after the previous animation ends, with an additional 0.5s delay (relative to previous end).
"-=0.5"Start 0.5s before the previous animation ends (overlapping the previous).
"<"Align with the start time of the previous animation (start together).
"<0.5"Start 0.5s after the previous animation has started.
">"Align with the end time of the previous animation (start when previous finishes).
">0.5"Start 0.5s after the previous animation has finished.

Control and Callback

let ctrlTween = null

const makeProgressTween = () => {
  gsap.killTweensOf("#ctrl-progress-fill")
  gsap.set("#ctrl-progress-fill", { width: "0%" })
  const lab = document.querySelector("#ctrl-progress-label")
  if (lab) lab.textContent = "0%"
  ctrlTween = gsap.to("#ctrl-progress-fill", {
    width: "100%",
    duration: 5,
    ease: "none",
    onUpdate() {
      const el = document.querySelector("#ctrl-progress-label")
      if (el) el.textContent = Math.round(this.progress() * 100) + "%"
    },
    onComplete() {
      const el = document.querySelector("#ctrl-progress-label")
      if (el) el.textContent = "100%"
    },
  })
}

makeProgressTween()

document.querySelector("#ctrl-btn-pause").onclick = () => { if (ctrlTween) ctrlTween.pause() }
document.querySelector("#ctrl-btn-resume").onclick = () => { if (ctrlTween) ctrlTween.resume() }
document.querySelector("#ctrl-btn-reverse").onclick = () => { if (ctrlTween) ctrlTween.reverse() }

document.querySelector("#ctrl-btn-seek-0").onclick = () => { if (ctrlTween) ctrlTween.seek(0) }
document.querySelector("#ctrl-btn-seek-25").onclick = () => { if (ctrlTween) ctrlTween.seek(1.25) }
document.querySelector("#ctrl-btn-seek-50").onclick = () => { if (ctrlTween) ctrlTween.seek(2.5) }

document.querySelector("#ctrl-btn-progress-half").onclick = () => { if (ctrlTween) ctrlTween.progress(0.5, false) }

document.querySelector("#ctrl-btn-ts-05").onclick = () => ctrlTween && (ctrlTween.timeScale(0.5))
document.querySelector("#ctrl-btn-ts-1").onclick = () => ctrlTween && (ctrlTween.timeScale(1))
document.querySelector("#ctrl-btn-ts-2").onclick = () => ctrlTween && (ctrlTween.timeScale(2))

document.querySelector("#ctrl-btn-restart").onclick = makeProgressTween

0%

Tags: GSAP