mcpField
A
awwwards-animations

awwwards-animations

github.com/devmartineseデザイン・UI152.9k 回インストール37 週間前に更新
リポジトリ

awwwards-animations について

Awwwards Animationsは、アニメーションを使ったWeb体験を作る開発者向けの、React中心のスキルです。演出や制作内容に応じたライブラリ選びに加え、実装パターン、パフォーマンス、アクセシビリティの確認方法を紹介します。

主な機能

  1. 01アニメーション内容に応じたライブラリ選び
  2. 02アニメーションツールのReactパターン
  3. 03スクロール演出とページ遷移のガイド
  4. 04SVG・Canvas・3Dの手法
  5. 05パフォーマンスとアクセシビリティの確認

活用シーン

  1. 01Webサイトへのスクロール連動演出の追加
  2. 02ページ遷移やテキスト表示アニメーションの作成
  3. 03ジェネレーティブ・幾何学・3D表現の制作
  4. 04アニメーション性能と動きを減らす設定の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/devmartinese/awwwards-animations-skill --skill awwwards-animations

インストール後、Claude Code で/awwwards-animationsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

.gitignore
README.md
references/
advanced-patterns.md
design-philosophy.md
animejs-react.md
audio-reactive.md
geometric-shapes.md
algorithmic-art.md
geometric-puzzles.md
performance.md
gsap-react.md
motion-patterns.md
lenis-react.md
physics-2d.md
text-effects.md

詳細

Awwwards アニメーション

Awwwards/FWA クオリティのプレミアムなウェブアニメーションを作成します。React ファーストのアプローチです。60fps は譲れません。

選択マトリクス

タスクライブラリ理由
スクロール駆動アニメーションGSAP + ScrollTrigger + useGSAP業界標準で、最も優れた制御性
スムーズスクロールLenis + ReactLenis最高のパフォーマンスで、ScrollTrigger と連携可能
React ネイティブアニメーションMotion (Framer Motion)React ネイティブで、useScroll/useTransform を使用
シンプル/軽量なエフェクトAnime.js 4.0軽量で、クリーンな API
複雑なタイムラインGSAP比類のないタイムライン制御
SVG モーフィングGSAP MorphSVG または Anime.jsどちらも優れています
3D + アニメーションThree.js + GSAPGSAP で Three.js オブジェクトを制御
ページ遷移AnimatePresence または GSAPReact には Motion、複雑な場合は GSAP
幾何学的な図形(ベクター)SVG + GSAP/Motionネイティブで、アニメーション可能
幾何学的な図形(キャンバス)Canvas 2D APIプログラムで生成でき、高速
疑似 3D 図形Zdogフラットデザインの 3D、約 2kb
クリエイティブコーディング/ジェネラティブp5.js豊富なエコシステム
オーディオリアクティブTone.jsWeb Audio、シンセ、エフェクト
2D 物理演算Matter.js重力、衝突、拘束
アルゴリズム/ジェネラティブアートCanvas 2D + p5.js数学に基づくビジュアル
フラクタル/L システムCanvas 2D 再帰描画再帰的な描画
テッセレーション/幾何学パズルSVG + GSAP正確なアニメーション変換
高度なキネティックタイポグラフィGSAP SplitText + Canvas文字単位で制御
グリッチエフェクトCSS + GSAPレイヤー化された RGB 分離、clip-path
ブルータリストアニメーションCSS raw + Motionハードカット、イージングなし
ミニマリストアニメーションMotion springs控えめで、意図的な動き

インストール(最新の安定版 - 2025)

# GSAP + React hook (v3.14.1)
npm install gsap @gsap/react

# Lenis (v1.3.17) - includes React components
npm install lenis

# Motion (Framer Motion)
npm install motion

# Anime.js (v4.0.0)
npm install animejs

React のセットアップ

1. GSAP の設定(アプリ全体)
// lib/gsap.ts
'use client' // Next.js App Router

import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import { useGSAP } from '@gsap/react'

// Register plugins once
gsap.registerPlugin(ScrollTrigger, useGSAP)

export { gsap, ScrollTrigger, useGSAP }
2. Lenis + GSAP ScrollTrigger の連携(重要)
// components/SmoothScroll.tsx
'use client'
import { ReactLenis, useLenis } from 'lenis/react'
import { useEffect } from 'react'
import { gsap, ScrollTrigger } from '@/lib/gsap'

export function SmoothScroll({ children }: { children: React.ReactNode }) {
  const lenis = useLenis()
  useEffect(() => {
    if (!lenis) return
    lenis.on('scroll', ScrollTrigger.update)
    gsap.ticker.add((time) => lenis.raf(time * 1000))
    gsap.ticker.lagSmoothing(0)
    return () => { gsap.ticker.remove(lenis?.raf) }
  }, [lenis])

  return (
    <ReactLenis root options={{ lerp: 0.1, duration: 1.2, smoothWheel: true }}>
      {children}
    </ReactLenis>
  )
}
// Wrap in layout: <SmoothScroll>{children}</SmoothScroll>

基本パターン(React)

詳細な実装については、リファレンスをご覧ください。

クイックパターン(React)

1. マグネティックカーソル(GSAP + useGSAP)
'use client'
import { useRef, useEffect } from 'react'
import { gsap, useGSAP } from '@/lib/gsap'

export function MagneticCursor() {
  const cursorRef = useRef<HTMLDivElement>(null)
  const pos = useRef({ x: 0, y: 0, cx: 0, cy: 0 })
  useEffect(() => {
    const h = (e: MouseEvent) => { pos.current.x = e.clientX; pos.current.y = e.clientY }
    window.addEventListener('mousemove', h)
    return () => window.removeEventListener('mousemove', h)
  }, [])
  useGSAP(() => {
    gsap.ticker.add(() => {
      const p = pos.current
      p.cx += (p.x - p.cx) * 0.15; p.cy += (p.y - p.cy) * 0.15
      gsap.set(cursorRef.current, { x: p.cx, y: p.cy })
    })
  })
  return <div ref={cursorRef} className="fixed w-10 h-10 border border-white rounded-full pointer-events-none mix-blend-difference z-[9999] -translate-x-1/2 -translate-y-1/2" />
}
2. マグネティックボタン(Motion)
'use client'
import { useRef, useState } from 'react'
import { motion } from 'motion/react'

export function MagneticButton({ children }: { children: React.ReactNode }) {
  const ref = useRef<HTMLButtonElement>(null)
  const [pos, setPos] = useState({ x: 0, y: 0 })
  const onMove = (e: React.MouseEvent) => {
    const { left, top, width, height } = ref.current!.getBoundingClientRect()
    setPos({ x: (e.clientX - left - width / 2) * 0.3, y: (e.clientY - top - height / 2) * 0.3 })
  }
  return (
    <motion.button ref={ref} onMouseMove={onMove} onMouseLeave={() => setPos({ x: 0, y: 0 })}
      animate={pos} transition={{ type: 'spring', stiffness: 150, damping: 15 }}
      className="px-8 py-4 bg-white text-black rounded-full">{children}</motion.button>
  )
}
3. パララックスヒーロー(GSAP + useGSAP)
'use client'
import { useRef } from 'react'
import { gsap, ScrollTrigger, useGSAP } from '@/lib/gsap'

export function ParallaxHero() {
  const containerRef = useRef<HTMLDivElement>(null)

  useGSAP(() => {
    gsap.to('.parallax-bg', {
      yPercent: 50,
      ease: 'none',
      scrollTrigger: {
        trigger: containerRef.current,
        start: 'top top',
        end: 'bottom top',
        scrub: true,
      },
    })

    gsap.to('.hero-title', {
      yPercent: 100,
      opacity: 0,
      scrollTrigger: {
        trigger: containerRef.current,
        start: 'top top',
        end: '50% top',
        scrub: true,
      },
    })
  }, { scope: containerRef })

  return (
    <div ref={containerRef} className="relative h-screen overflow-hidden">
      <div className="parallax-bg absolute inset-0 bg-cover bg-center" />
      <h1 className="hero-title absolute inset-0 flex items-center justify-center text-6xl">
        Hero Title
      </h1>
    </div>
  )
}
4. 文字単位のテキスト表示(Motion)
'use client'
import { motion } from 'motion/react'

const container = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: { staggerChildren: 0.02 },
  },
}

const child = {
  hidden: { opacity: 0, y: 50, rotateX: -90 },
  visible: {
    opacity: 1,
    y: 0,
    rotateX: 0,
    transition: { type: 'spring', damping: 12 },
  },
}

export function TextReveal({ text }: { text: string }) {
  return (
    <motion.span
      variants={container}
      initial="hidden"
      whileInView="visible"
      viewport={{ once: true }}
      className="inline-block"
    >
      {text.split('').map((char, i) => (
        <motion.span key={i} variants={child} className="inline-block">
          {char === ' ' ? '\u00A0' : char}
        </motion.span>
      ))}
    </motion.span>
  )
}
5. 画像表示エフェクト(GSAP)
'use client'
import { useRef } from 'react'
import { gsap, useGSAP } from '@/lib/gsap'

export function ImageReveal({ src, alt }: { src: string; alt: string }) {
  const containerRef = useRef<HTMLDivElement>(null)

  useGSAP(() => {
    gsap.from(containerRef.current, {
      clipPath: 'inset(100% 0% 0% 0%)',
      duration: 1.2,
      ease: 'power4.inOut',
      scrollTrigger: {
        trigger: containerRef.current,
        start: 'top 80%',
      },
    })

    gsap.from('.reveal-img', {
      scale: 1.3,
      duration: 1.5,
      ease: 'power2.out',
      scrollTrigger: {
        trigger: containerRef.current,
        start: 'top 80%',
      },
    })
  }, { scope: containerRef })

  return (
    <div ref={containerRef} className="overflow-hidden">
      <img src={src} alt={alt} className="reveal-img w-full h-full object-cover" />
    </div>
  )
}
6. グリッチテキストエフェクト(CSS + GSAP)
'use client'
import { useRef, useEffect } from 'react'
import { gsap } from '@/lib/gsap'

export function GlitchText({ text }: { text: string }) {
  const ref = useRef<HTMLDivElement>(null)

  useEffect(() => {
    const layers = ref.current!.querySelectorAll('.g-layer')
    const tl = gsap.timeline({ repeat: -1, repeatDelay: 3 })
    tl.to(layers[0], { x: -5, duration: 0.05, ease: 'none' }, 0)
      .to(layers[0], { x: 5, duration: 0.05 }, 0.05)
      .to(layers[0], { x: 0, duration: 0.05 }, 0.1)
      .to(layers[1], { x: 5, duration: 0.05 }, 0.02)
      .to(layers[1], { x: -5, duration: 0.05 }, 0.07)
      .to(layers[1], { x: 0, duration: 0.05 }, 0.12)
    return () => { tl.kill() }
  }, [])

  return (
    <div ref={ref} className="relative font-mono text-5xl font-black">
      <span className="relative z-10">{text}</span>
      <span className="g-layer absolute inset-0 text-cyan-400 mix-blend-multiply" aria-hidden>{text}</span>
      <span className="g-layer absolute inset-0 text-red-400 mix-blend-multiply" aria-hidden>{text}</span>
    </div>
  )
}
7. フラクタルツリー(Canvas 2D)
'use client'
import { useRef, useEffect } from 'react'

export function FractalTree({ depth = 10, angle = 25 }: { depth?: number; angle?: number }) {
  const canvasRef = useRef<HTMLCanvasElement>(null)

  useEffect(() => {
    const canvas = canvasRef.current!
    const ctx = canvas.getContext('2d')!
    canvas.width = canvas.offsetWidth * 2; canvas.height = canvas.offsetHeight * 2; ctx.scale(2, 2)
    let progress = 0, raf = 0

    function branch(x: number, y: number, len: number, a: number, d: number) {
      if (d > depth || len < 2) return
      const dp = Math.max(0, Math.min(1, progress * depth - d))
      if (dp <= 0) return
      const ex = x + Math.cos(a * Math.PI / 180) * len * dp
      const ey = y - Math.sin(a * Math.PI / 180) * len * dp
      ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(ex, ey)
      ctx.strokeStyle = `hsl(${120 + d * 15}, 60%, ${30 + d * 5}%)`
      ctx.lineWidth = Math.max(1, (depth - d) * 1.5); ctx.stroke()
      branch(ex, ey, len * 0.72, a + angle, d + 1)
      branch(ex, ey, len * 0.72, a - angle, d + 1)
    }
    const animate = () => {
      progress = Math.min(1, progress + 0.008)
      ctx.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight)
      branch(canvas.offsetWidth / 2, canvas.offsetHeight, canvas.offsetHeight * 0.28, 90, 0)
      if (progress < 1) raf = requestAnimationFrame(animate)
    }
    animate()
    return () => cancelAnimationFrame(raf)
  }, [depth, angle])
  return <canvas ref={canvasRef} className="w-full h-full bg-gray-950" />
}

L システム、フローフィールド、アトラクター、ノイズ、神聖幾何学については、references/algorithmic-art.md をご覧ください。

8. 幾何学的な分割(SVG + GSAP)
'use client'
import { useRef, useState } from 'react'
import { gsap } from '@/lib/gsap'

const P = [
  { id: 'A', tri: 'M 0,173 L 50,87 L 100,173 Z', sq: 'M 0,0 L 100,0 L 100,87 L 0,87 Z', c: '#f43f5e' },
  { id: 'B', tri: 'M 50,87 L 100,0 L 150,87 Z', sq: 'M 100,0 L 200,0 L 200,87 L 100,87 Z', c: '#8b5cf6' },
  { id: 'C', tri: 'M 100,173 L 150,87 L 200,173 Z', sq: 'M 0,87 L 100,87 L 100,173 L 0,173 Z', c: '#06b6d4' },
  { id: 'D', tri: 'M 50,87 L 100,173 L 150,87 L 100,0 Z', sq: 'M 100,87 L 200,87 L 200,173 L 100,173 Z', c: '#f59e0b' },
]
export function GeometricDissection() {
  const svg = useRef<SVGSVGElement>(null)
  const [isSq, setSq] = useState(false)
  const morph = () => {
    const t = !isSq
    P.forEach((p, i) => {
      const el = svg.current!.querySelector(`#d-${p.id}`)
      if (el) gsap.to(el, { attr: { d: t ? p.sq : p.tri }, duration: 1.5, ease: 'power2.inOut', delay: i * 0.15 })
    }); setSq(t)
  }
  return (
    <div className="flex flex-col items-center gap-4">
      <svg ref={svg} viewBox="-10 -10 220 200" className="w-64 h-64">
        {P.map(p => <path key={p.id} id={`d-${p.id}`} d={p.tri} fill={p.c} stroke="#000" strokeWidth="1.5" />)}
      </svg>
      <button onClick={morph} className="px-6 py-2 bg-white text-black font-mono text-sm">{isSq ? '△' : '□'}</button>
    </div>
  )
}

タンラム、テッセレーション、ペンローズタイル、ポリオミノについては、references/geometric-puzzles.md をご覧ください。

9. ブルータリストグリッド(モーション)
'use client'
import { motion } from 'motion/react'

export function BrutalistGrid({ items }: { items: string[] }) {
  return (
    <div className="grid grid-cols-3 border-2 border-black">
      {items.map((item, i) => (
        <motion.div key={i}
          className="border-2 border-black p-6 font-mono font-black uppercase text-2xl"
          style={{ mixBlendMode: i % 2 === 0 ? 'normal' : 'difference' }}
          initial={{ opacity: 0 }} whileInView={{ opacity: 1 }} viewport={{ once: true }}
          transition={{ duration: 0, delay: i * 0.1 }}
          whileHover={{ backgroundColor: '#000', color: '#BAFF39', transition: { duration: 0 } }}
        >{item}</motion.div>
      ))}
    </div>
  )
}

デザイン哲学(クイックリファレンス)

スタイルモーションの印象イージングタイポグラフィ主な特徴
ブルータリスト強烈、瞬時、ぎくしゃくnone / steps()モノスペース、15-30vw飾らない率直さ
ミニマリスト滑らか、控えめ、ゆっくりpower2.outサンセリフのライト体意図的な抑制
アブストラクトノイズ駆動、パラメトリックオーガニック/正弦波さまざま数学的な美しさ
ネオブルータリスト大胆ながらも抑制されたpower1.outモノスペース+カラーブルータリズム+抑制

カラーパレットや組み合わせの手法を含む詳しいガイドは、references/design-philosophy.md をご覧ください。

イージングリファレンス

印象GSAPMotion
滑らかpower2.out[0.16, 1, 0.3, 1]
キビキビpower4.out[0.87, 0, 0.13, 1]
弾むようなback.out(1.7){ type: 'spring', stiffness: 300, damping: 20 }
ドラマチックpower4.inOut[0.76, 0, 0.24, 1]

タイミング

  • マイクロインタラクション:150-300ms
  • UI トランジション:300-500ms
  • ページトランジション:500-800ms
  • スタガー:アイテムごとに0.02-0.1s

アクセシビリティ

// Motion: useReducedMotion() → conditionally disable/reduce animations
import { useReducedMotion } from 'motion/react'
const reduced = useReducedMotion() // true if prefers-reduced-motion: reduce
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

パフォーマンスのルール

  1. アニメーションにはtransformとopacityのみを使用します
  2. will-changeは控えめに使用します
  3. 必ずクリーンアップします:useGSAPが自動的に処理します
  4. GSAPセレクターのスコープをコンテナのrefに限定します
  5. GSAPを使うイベントハンドラーにはcontextSafe()を使用します
  6. Motionのバリアントオブジェクトをメモ化します

よくある落とし穴

  1. LenisとScrollTriggerを連携していない
  2. useGSAPにscopeがない
  3. クリックハンドラーにcontextSafe()を使用していない
  4. React 18 Strict Modeによってエフェクトが2回呼び出される
  5. Next.js App Routerで'use client'を忘れる
  6. 動的コンテンツの後にScrollTrigger.refresh()を呼び出していない

テストチェックリスト

  • スクロール時に60fps(Chrome DevTools Performance)
  • キーボードでナビゲーションできる
  • prefers-reduced-motionの設定に従う
  • レイアウトシフトがない(CLS)
  • モバイルでタッチ操作が機能する
  • 本番環境でScrollTriggerのマーカーが削除されている
  • アンマウント時にメモリーリークがない

インスピレーション

Active Theory、Studio Freight、Locomotive、Resn、Aristide Benoist、Immersive Garden

コメント