17
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?

【アニメーション】Next.jsでぬるぬるアニメーションでぬるぬるする【Lenis】【FramerMotion】

Last updated at Posted at 2025-12-16

この記事は デジタル創作サークル UniProject Advent Calendar 2025 18 日目の記事です。

前書き

ある日、Twitterを眺めていると、「新しいホームページを作った」という投稿を見つけたので見てみたら、めっちゃぬるぬる動くアニメーションで感動しました。
私のホームページもまさにそういう「ぬるぬるアニメーション」を追求する場所なので、早速真似して遊んでみました。
使われている技術とかは、有名なWappalyzerで調べてみました。
それの技術二つをまとめてみました。

本編

使った物

  • Next.js
  • Lenis
  • FramerMotion

ここらへんを使うとサイトのパフォーマンススコア一気に落ちる可能性があるので、そこらへんは、まあ、気合勇気元気で乗り越えてください。

Lenisのスクロール編

Lenisはスクロールとかをいじれるやつです。
よくスマホのスクロールである。
スクロールしたらすぐ止まらずに減速していくあれ、慣性の法則ですね。
あれを再現できます。

インストール

# npm
npm i lenis

# yarn
yarn add lenis

# bun
bun add lenis

セットアップ

これでぬるぬるとしたスクロールが実現できます。

/components/lenis.tsx
"use client";

import { useEffect } from "react";
import Lenis from "lenis";

export const LenisProvider = () => {
  useEffect(() => {
    const lenis = new Lenis({});

    const raf = (time: number) => {
      lenis.raf(time);
      requestAnimationFrame(raf);
    };

    requestAnimationFrame(raf);

    return () => {
      lenis.destroy();
    };
  }, []);

  return null;
};
/app/layout.tsx
import React from "react";
import { LenisProvider } from "@/components/lenis";

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="ja">
      <body>
        <LenisProvider />
        {children}
      </body>
    </html>
  );
}

FramerMotionのアニメーション編

FramerMotionのアニメーションは結構いろんなことできます。
ホバーしたときになにをするかとか、いろいろできます。
まあ、しいて言えば重たいですね。
パフォーマンススコア下がりがちなので、気を付けるべきです。

インストール

# npm
npm install motion

# yarn
yarn add motion

# bun
bun add motion

基本的なコード

<motion.button
  whileHover={{ scale: 1.1, rotate: -10, transition: { duration: 0.3 } }}
  whileTap={{ scale: 0.9, transition: { duration: 0.3 } }}
/>

scaleはどこのくらい大きくするか、小さくするか、1を基準としています。
rotateはどのくらい回転させるかです、360度を使います、マイナスも使えますよ。
durationはどのくらいかけて移動させるかみたいな感じにできます。

ページ遷移のアニメーション

これを使うとページ遷移したときに下から要素がでてくる感じになります。

/components/PageTransition.tsx
"use client";

import { motion, AnimatePresence } from "framer-motion";
import { usePathname } from "next/navigation";
import { useEffect, useState } from "react";

export function PageTransition({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const pathname = usePathname();

  const [isMounted, setIsMounted] = useState(false);

  useEffect(() => {
    setIsMounted(true);
  }, []);

  if (!isMounted) {
    return <>{children}</>;
  }
  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={pathname}
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        exit={{ opacity: 0, y: -20 }}
        transition={{
          duration: 0.3,
          ease: "easeInOut",
        }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  );
}
/app/layout.tsx
import React from "react";
import { LenisProvider } from "@/components/PageTransition";

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="ja">
      <body>
        <PageTransition>
        {children}
        </PageTransition>
      </body>
    </html>
  );
}

終わりに

私のホームページは全部導入していて、FramerMotionでアイコンをクリックしたときの細かなアニメーションなども作っています。
あと、これ全部するとGoogle Pagespeed insightsのパフォーマンススコアがめっちゃ低下します。
私のホームページはそのおかげでモバイルのスコアが50程度しかありません。
これはほかにもいろいろな機能を搭載してるからであって、この二つを導入したからってわけではないと思いますが、まあまあ、スコアは低下しやすいです。

おまけ

プログレスバーもついでにかいときます。
bprogressを使います

インストール

# npm
npm install @bprogress/next

# yarn
yarn add @bprogress/next

# bun
bun add @bprogress/next

セットアップ

heightはプログレスバーの高さ、colorはプログレスバーの色、簡単に設定できます。

/components/ProgressBarProvider.tsx
"use client";

import { ProgressProvider } from "@bprogress/next/app";

const ProgressProviders = ({ children }: { children: React.ReactNode }) => {
  return (
    <ProgressProvider
      height="2px"
      color="#2299DD"
      options={{ showSpinner: false }}
      shallowRouting
    >
      {children}
    </ProgressProvider>
  );
};

export default ProgressProviders;
/app/layout.tsx
import React from "react";
import { LenisProvider } from "@/components/ProgressBarProvider";

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="ja">
      <body>
        <ProgressProviders>
        {children}
        </ProgressProviders>
      </body>
    </html>
  );
}

全部使うとこんなかんじ

/app/layout.tsx
import React from "react";
import { LenisProvider } from "@/components/lenis";
import ProgressProviders from "@/components/ProgressBarProvider";
import { PageTransition } from "@/components/PageTransition";

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="ja">
      <body>
        <LenisProvider />
        <ProgressProviders>
          <PageTransition>{children}</PageTransition>
        </ProgressProviders>
      </body>
    </html>
  );
}
17
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
17
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?