0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React + Framer Motion】viewportに映ったタイミングでアニメーションを発火させる

Last updated at Posted at 2025-03-29

はじめに

サイト制作で記事のリンク一覧を作成した。
各リンク一覧がviewportに映ったら、サムネイル画像が下から上に表示されるようにしたい。

そのためには、サムネイル画像の上に同じ形の黒い長方形を用意して、
映ったタイミングでそれを上へスライドさせるという実装をする必要があった。

これを Framer Motion を使って実装する。

Framer Motionとは?

Framer Motion は React 向けのアニメーションライブラリ。

initialanimate などの予約語を使うことで、状態ごとにアニメーションを管理できるのがとても便利。

まずはインストール。

npm install framer-motion

実装の概要

やりたいことは次の通り:

  1. サムネイル画像の上に黒いオーバーレイ(div)を重ねる
  2. Framer Motion で、オーバーレイが下から上にスライドするアニメーションを定義
  3. そのオーバーレイが viewport に入ったタイミングでアニメーション発火

実装コード(React + Framer Motion)

AnimationLink.tsxというコンポーネントを作成した
これに記事データのpropsを渡して使用している

'use client'

import { motion } from 'framer-motion'
import styles from './articleLink.module.scss'

const overlayVariants = {
  hidden: { y: '0%' },
  visible: (custom: number) => ({
    y: '-100%',
    transition: {
      delay: custom % 2 === 0 ? 0 : 0.2,
      duration: 0.6,
      ease: [0.2, 0.34, 0.24, 1],
    },
  }),
}

type Props = {
  id: string
  title: string
  thumbnail_path: string
  custom: number
}

export default function ArticleLink({ id, title, thumbnail_path, custom }: Props) {

  return (
    <div className={styles.item}>
      <div className={styles.imageWrapper}>
        <Link href={`/production/${id}`}>
          <div
            className={styles.thumbnailContainer}
          >
            <img
              src={thumbnail_path}
              alt={title}
            />
          </div>
        </Link>

        <motion.div
          className={styles.overlay}
          variants={overlayVariants}
          initial="hidden"
          whileInView="visible"
          viewport={{ once: true, amount: 0.8 }}
          custom={custom}
        />
      </div>
      <h2 className={styles.title}>{title}</h2>
    </div>
  )
}

ポイント解説

variants でアニメーション状態を定義

const overlayVariants = {
  hidden: { y: '0%' },
  visible: (custom: number) => ({
    y: '-100%',
    transition: {
      delay: custom % 2 === 0 ? 0 : 0.2,
      duration: 0.6,
      ease: [0.2, 0.34, 0.24, 1],
    },
  }),
}

このように、状態を「名前付き」で管理できる
hidden → visible に切り替えることで、要素の動きを制御している

whileInView でスクロール時の表示検知

<motion.div
  initial="hidden"
  whileInView="visible"
  viewport={{ once: true, amount: 0.8 }}
/>
  • initial="hidden":初期状態
  • whileInView="visible":viewport に 80% 入ったら visible 状態へ切り替え
  • once: true:一度だけアニメーションする

80%に設定した理由は、少しでもビューポートに映るとすぐアニメーションが開始されてしまうため

おわりに

直感的にアニメーション実装ができたので今後も使っていきたい

備忘録として使用頻度が高そうなものをまとめた

プロパティ名 カテゴリ 説明
initial 基本 初期状態のスタイル(アニメーション開始前)
animate 基本 アニメーションの最終(または途中)状態
exit 基本 コンポーネントが消える時の状態(AnimatePresenceと併用)
transition 基本 アニメーションの速度・タイミング設定
whileHover ジェスチャー ホバー中の状態に変化させる
whileTap ジェスチャー クリック/タップ中の状態に変化させる
whileFocus ジェスチャー フォーカス中の状態(アクセシビリティ対応)
whileDrag ジェスチャー ドラッグ中の状態に変化させる
variants 状態制御 状態(初期・アニメ・終了)をまとめて定義できるオブジェクト
drag 動作制御 要素をドラッグ可能にする(true / x / y
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?