gsap-svg

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GSAP SVG — Vue / Nuxt Patterns

GSAP SVG — Vue / Nuxt 实践方案

Flow: gsap-setup → gsap-animate → gsap-svg → gsap-optimise → gsap-test
Companion: For DrawSVG/MorphSVG API reference, invoke gsap-plugins. This skill covers SVG animation recipes only. Requires:
greensock/gsap-skills

流程: gsap-setup → gsap-animate → gsap-svg → gsap-optimise → gsap-test
配套内容: 如需DrawSVG/MorphSVG的API参考,请调用gsap-plugins。本技能仅涵盖SVG动画实践方案。依赖:
greensock/gsap-skills

1. SVG Path Drawing (strokeDashoffset)

1. SVG路径绘制(strokeDashoffset)

Manual approach

手动实现方式

js
const ctx = gsap.context(() => {
  const paths = containerRef.value.querySelectorAll('path')
  paths.forEach((path) => {
    const len = path.getTotalLength()
    gsap.set(path, { strokeDasharray: len, strokeDashoffset: len })
  })
  gsap.to(paths, {
    strokeDashoffset: 0, duration: 2, ease: 'power2.inOut',
    stagger: { each: 0.08, from: 'start' },
  })
}, containerRef.value)
js
const ctx = gsap.context(() => {
  const paths = containerRef.value.querySelectorAll('path')
  paths.forEach((path) => {
    const len = path.getTotalLength()
    gsap.set(path, { strokeDasharray: len, strokeDashoffset: len })
  })
  gsap.to(paths, {
    strokeDashoffset: 0, duration: 2, ease: 'power2.inOut',
    stagger: { each: 0.08, from: 'start' },
  })
}, containerRef.value)

With DrawSVG plugin (lazy-loaded)

使用DrawSVG插件(懒加载)

js
await $lazyLoadDrawSVG()
gsap.from(paths, { drawSVG: '0%', duration: 2, stagger: 0.05 })
gsap.to(paths, { drawSVG: '40% 80%', duration: 1.5 }) // partial range
js
await $lazyLoadDrawSVG()
gsap.from(paths, { drawSVG: '0%', duration: 2, stagger: 0.05 })
gsap.to(paths, { drawSVG: '40% 80%', duration: 1.5 }) // partial range

Staggered paths then nodes

路径 stagger 动画后执行节点动画

js
const tl = gsap.timeline()
tl.to(paths, { strokeDashoffset: 0, duration: 1.5, ease: 'power2.inOut', stagger: { each: 0.05 } })
  .from(circles, { scale: 0, autoAlpha: 0, duration: 0.6, ease: 'back.out(2)',
    stagger: { each: 0.03, from: 'random' } }, '-=0.5')
js
const tl = gsap.timeline()
tl.to(paths, { strokeDashoffset: 0, duration: 1.5, ease: 'power2.inOut', stagger: { each: 0.05 } })
  .from(circles, { scale: 0, autoAlpha: 0, duration: 0.6, ease: 'back.out(2)',
    stagger: { each: 0.03, from: 'random' } }, '-=0.5')

Infinite pulse ring

无限循环脉冲环

js
gsap.fromTo(ringEl,
  { scale: 1, autoAlpha: 0.7 },
  { scale: 2.2, autoAlpha: 0, duration: 2, ease: 'power1.out', repeat: -1, force3D: true }
)

js
gsap.fromTo(ringEl,
  { scale: 1, autoAlpha: 0.7 },
  { scale: 2.2, autoAlpha: 0, duration: 2, ease: 'power1.out', repeat: -1, force3D: true }
)

2. SVG Morphing (MorphSVGPlugin)

2. SVG变形(MorphSVGPlugin)

js
const pathTo = pathEl.value.dataset.pathTo
gsap.to(pathEl.value, { attr: { d: pathTo }, duration: 1.5, ease: 'power2.inOut' })

// Scroll-driven
gsap.to(pathEl.value, {
  attr: { d: pathTo }, ease: 'none',
  scrollTrigger: { trigger: sectionRef.value, start: 'top center', end: 'bottom center', scrub: 0.5 },
})

// With MorphSVGPlugin
await $lazyLoadMorphSVG()
gsap.to(pathEl.value, { morphSVG: targetPathEl.value, duration: 1.5 })

js
const pathTo = pathEl.value.dataset.pathTo
gsap.to(pathEl.value, { attr: { d: pathTo }, duration: 1.5, ease: 'power2.inOut' })

// Scroll-driven
gsap.to(pathEl.value, {
  attr: { d: pathTo }, ease: 'none',
  scrollTrigger: { trigger: sectionRef.value, start: 'top center', end: 'bottom center', scrub: 0.5 },
})

// With MorphSVGPlugin
await $lazyLoadMorphSVG()
gsap.to(pathEl.value, { morphSVG: targetPathEl.value, duration: 1.5 })

3. Scallop Wave

3. 扇形波浪

js
gsap.from(ellipses, {
  scale: 0, autoAlpha: 0, transformOrigin: 'top center',
  duration: 0.8, ease: 'back.out(3)', force3D: true,
  stagger: { each: 0.04, from: 'center' },
  scrollTrigger: { trigger: containerRef.value, start: 'top 80%', toggleActions: 'play none none reverse' },
})

js
gsap.from(ellipses, {
  scale: 0, autoAlpha: 0, transformOrigin: 'top center',
  duration: 0.8, ease: 'back.out(3)', force3D: true,
  stagger: { each: 0.04, from: 'center' },
  scrollTrigger: { trigger: containerRef.value, start: 'top 80%', toggleActions: 'play none none reverse' },
})

4. Critical Gotchas

4. 关键注意事项

fill: transparent NOT fill: none

使用 fill: transparent 而非 fill: none

none
means "unpainted" — GSAP cannot interpolate from it. Use
transparent
.
none
表示“未绘制”——GSAP无法对其进行插值。请使用
transparent

Separate animation concern ownership

分离动画属性的控制权

Concurrent timelines must own different properties. Wave owns stroke. Color owns fill + autoAlpha.
并行时间线必须控制不同的属性。波浪动画控制描边,颜色动画控制填充与autoAlpha。

Absolute timeline positioning

时间线绝对定位

Use
.add(animation, timeInSeconds)
not relative offsets for multi-layer compositions.
对于多层合成动画,请使用
.add(animation, timeInSeconds)
而非相对偏移量。

SVG DOM cleanup

SVG DOM清理

js
onUnmounted(() => {
  ctx?.revert()
  circuitWrap.value?.replaceChildren() // clear SVG DOM
})

js
onUnmounted(() => {
  ctx?.revert()
  circuitWrap.value?.replaceChildren() // clear SVG DOM
})

References

参考资料

  • references/svg-patterns.md
    — Circuit Tree triple-layer animation, CTAHud 5-layer system, Dynamic Morphing shape overlays, Smooth Morph with interpolation
  • references/svg-patterns.md
    — 电路树三层动画、CTAHud五层系统、动态变形形状叠加、带插值的平滑变形