はじめに
ウェブサイトに動きのある要素を加えることで、ユーザーの注目を集め、印象的なユーザー体験を提供することができます。今回は、V0と共にNext.jsとReactを使用して、ハート型に集まるパーティクルアニメーションを作成する方法を詳しく解説します。
このチュートリアルでは、以下の技術やコンセプトを扱います:
- Next.jsのApp Router
- Reactのuseeffectとuseref
- HTML5 Canvas API
- JavaScriptのアニメーション
プロジェクトのセットアップ
まず、新しいNext.jsプロジェクトを作成します:
npx create-next-app@latest heart-particles-app
cd heart-particles-app
以下のファイル構造です。
heart-particles-app/
├── app/
│ ├── layout.tsx
│ └── heart-particles/
│ └── page.tsx
├── package.json
└── tsconfig.json
必要なファイルの作成と編集
1. app/layout.tsx
このファイルは、アプリケーション全体のレイアウトを定義します。
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className="bg-black">{children}</body>
</html>
)
}
2. app/heart-particles/page.tsx
このファイルにハートパーティクルアニメーションのコンポーネントを実装します。
"use client"
import React, { useRef, useEffect } from 'react'
export default function Component() {
const canvasRef = useRef<HTMLCanvasElement>(null)
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
const ctx = canvas.getContext('2d')
if (!ctx) return
const setCanvasSize = () => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
}
setCanvasSize()
class Particle {
x: number
y: number
size: number
targetX: number
targetY: number
color: string
speed: number
angle: number
rising: boolean
constructor(x: number, y: number, targetX: number, targetY: number) {
this.x = x
this.y = y
this.size = Math.random() * 2 + 1
this.targetX = targetX
this.targetY = targetY
this.color = `rgba(0, ${Math.floor(Math.random() * 55 + 200)}, ${Math.floor(Math.random() * 55 + 200)}, ${Math.random() * 0.3 + 0.5})`
this.speed = Math.random() * 0.5 + 0.2
this.angle = Math.random() * Math.PI * 2
this.rising = false
}
update(time: number) {
if (!this.rising && Math.random() < 0.01) {
this.rising = true
}
if (this.rising) {
const dx = this.targetX - this.x
const dy = this.targetY - this.y
const distance = Math.sqrt(dx * dx + dy * dy)
if (distance > 1) {
this.x += dx * this.speed * 0.01
this.y += dy * this.speed * 0.01
} else {
this.x = this.targetX
this.y = this.targetY
}
} else {
this.y += Math.sin(this.angle + time * 2) * 0.2
}
}
draw() {
ctx!.fillStyle = this.color
ctx!.beginPath()
ctx!.arc(this.x, this.y, this.size, 0, Math.PI * 2)
ctx!.fill()
}
}
function createHeart() {
const particles: Particle[] = []
const centerX = canvas.width / 2
const centerY = canvas.height / 2
const size = Math.min(canvas.width, canvas.height) * 0.3
for (let i = 0; i < 3000; i++) {
const t = i / 3000 * Math.PI * 2
const x = 16 * Math.pow(Math.sin(t), 3)
const y = -(13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t))
const targetX = centerX + x * size / 16
const targetY = centerY + y * size / 16
const startX = Math.random() * canvas.width
const startY = canvas.height + Math.random() * 50 // Start below the canvas
particles.push(new Particle(startX, startY, targetX, targetY))
}
return particles
}
let particles = createHeart()
let time = 0
function animate() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'
ctx.fillRect(0, 0, canvas.width, canvas.height)
time += 0.01
particles.forEach(particle => {
particle.update(time)
particle.draw()
})
requestAnimationFrame(animate)
}
animate()
const handleResize = () => {
setCanvasSize()
particles = createHeart()
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
return (
<canvas
ref={canvasRef}
className="w-full h-screen bg-black"
/>
)
}
コードの詳細解説
1. app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className="bg-black">{children}</body>
</html>
)
}
このファイルは、アプリケーション全体のレイアウトを定義します。body
タグにbg-black
クラスを追加することで、背景を黒に設定しています。
2. app/heart-particles/page.tsx
コンポーネントの基本構造
"use client"
import React, { useRef, useEffect } from 'react'
export default function Component() {
const canvasRef = useRef<HTMLCanvasElement>(null)
useEffect(() => {
// ...
}, [])
return (
<canvas
ref={canvasRef}
className="w-full h-screen bg-black"
/>
)
}
-
"use client"
ディレクティブは、このコンポーネントがクライアントサイドでレンダリングされることを示します。 -
useRef
フックを使用して、canvasへの参照を作成します。 -
useEffect
フックは、コンポーネントのマウント時に一度だけ実行されます。 - canvasは画面全体を覆うように設定されています。
Particleクラス
class Particle {
// ...
constructor(x: number, y: number, targetX: number, targetY: number) {
// ...
}
update(time: number) {
// ...
}
draw() {
// ...
}
}
このクラスは各パーティクルの動作を定義します:
-
constructor
: パーティクルの初期位置、目標位置、色、速度などを設定します。 -
update
: パーティクルの位置を更新します。最初はゆっくり上昇し、その後ハートの形に向かって移動します。 -
draw
: パーティクルをキャンバス上に描画します。
ハートの形状生成
function createHeart() {
// ...
for (let i = 0; i < 3000; i++) {
const t = i / 3000 * Math.PI * 2
const x = 16 * Math.pow(Math.sin(t), 3)
const y = -(13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t))
// ...
}
// ...
}
この関数は、数学的な方程式を使用してハート型の座標を生成します。3000個のパーティクルを作成し、それぞれにハート型の目標位置を割り当てます。
アニメーション
function animate() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'
ctx.fillRect(0, 0, canvas.width, canvas.height)
time += 0.01
particles.forEach(particle => {
particle.update(time)
particle.draw()
})
requestAnimationFrame(animate)
}
animate
関数は、各フレームでパーティクルの位置を更新し、再描画します。requestAnimationFrame
を使用することで、ブラウザの描画タイミングに合わせて効率的にアニメーションを実行します。
リサイズ対応
const handleResize = () => {
setCanvasSize()
particles = createHeart()
}
window.addEventListener('resize', handleResize)
ウィンドウのリサイズイベントをリッスンし、キャンバスのサイズとハートの形を適切に調整します。
パフォーマンスの考慮点
- パーティクルの数(3000個)は、多くのデバイスで滑らかなアニメーションを実現できる数として選ばれていますが、必要に応じて調整可能です。
-
requestAnimationFrame
を使用することで、ブラウザのレンダリングサイクルに合わせて効率的にアニメーションを実行しています。 - 背景を完全に消去せず、半透明の黒で塗りつぶすことで、パーティクルの軌跡を表現しつつ、描画コストを抑えています。
カスタマイズの可能性
このコードは様々な方法でカスタマイズできます:
- パーティクルの色や数を変更
- ハートの形状を他の形に変更(数式を調整することで)
- パーティクルの動きのパターンを変更(例:螺旋状の動きを追加)
- 背景色や効果の追加(グラデーションなど)
詳細な実行方法
- Next.jsプロジェクトを作成します:
npx create-next-app@latest heart-particles-app
cd heart-particles-app
- 必要なファイルを作成します:
mkdir -p app/heart-particles
touch app/heart-particles/page.tsx
-
app/layout.tsx
とapp/heart-particles/page.tsx
の内容を、上記で提供したコードで置き換えます。 - 開発サーバーを起動します:
npm run dev
- ブラウザで
http://localhost:3000/heart-particles
にアクセスします。 - ハートの形に集まるパーティクルアニメーションが表示されるはずです。
注意点
- このコンポーネントは、モダンブラウザでのみ正常に動作します。
- 大量のパーティクルを描画するため、低スペックのデバイスでは動作が遅くなる可能性があります。
まとめ
このチュートリアルでは、Next.jsとReactを使用して、魅力的なハートパーティクルアニメーションを作成する方法を学びました。HTML5 Canvasを活用し、数学的な方程式を用いてハート型を生成し、パーティクルの動きを制御することで、印象的なビジュアル効果を実現しました。
このような動的なグラフィックスは、ウェブサイトのランディングページや特別なイベントページなどで効果的に使用できます。ぜひ、このコードを基に、独自のアイデアを加えて、さらに魅力的なアニメーションを作成してみてください。