この記事は デジタル創作サークル 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
セットアップ
これでぬるぬるとしたスクロールが実現できます。
"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;
};
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はどのくらいかけて移動させるかみたいな感じにできます。
ページ遷移のアニメーション
これを使うとページ遷移したときに下から要素がでてくる感じになります。
"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>
);
}
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はプログレスバーの色、簡単に設定できます。
"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;
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>
);
}
全部使うとこんなかんじ
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>
);
}