Open2

Reactで紙吹雪アニメーションを作成する方法について

まさぴょんまさぴょん

Reactで紙吹雪アニメーションを作成する

Reactで紙吹雪のようなアニメーションを実装する時は、既存のライブラリを使用すると、簡単に紙吹雪のアニメーションを追加できます。
おすすめのライブラリをいくつか紹介します。

react-confetti

react-confettiは、画面全体に紙吹雪を表示するためのライブラリです。

インストール方法:

npm install react-confetti

使用例:

import React from 'react';
import Confetti from 'react-confetti';
import { useWindowSize } from 'react-use';

const ConfettiComponent = () => {
  const { width, height } = useWindowSize();

  return <Confetti width={width} height={height} />;
};

export default ConfettiComponent;

react-dom-confetti

react-dom-confettiは、特定の要素から紙吹雪が飛び出すような効果を実現できます。

インストール方法:

npm install react-dom-confetti

使用例:

import React, { useState } from 'react';
import Confetti from 'react-dom-confetti';

const ConfettiButton = () => {
  const [active, setActive] = useState(false);

  const handleClick = () => {
    setActive(true);
    // 一定時間後に非表示にする
    setTimeout(() => setActive(false), 3000);
  };

  return (
    <div>
      <button onClick={handleClick}>お祝いする</button>
      <Confetti active={active} />
    </div>
  );
};

export default ConfettiButton;

カスタム実装

ライブラリを使わずに、自分で紙吹雪アニメーションを実装することも可能です。CanvasやCSSアニメーションを使用して、独自のエフェクトを作成できます。

例: Canvasを使用した実装

import React, { useRef, useEffect } from 'react';

const CustomConfetti = () => {
  const canvasRef = useRef(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    const context = canvas.getContext('2d');
    // キャンバスの初期設定やアニメーションのロジックをここに追加
  }, []);

  return <canvas ref={canvasRef} width={window.innerWidth} height={window.innerHeight} />;
};

export default CustomConfetti;

ただし、カスタム実装は手間がかかるため、まずは既存のライブラリを使用することをおすすめします。

これらの方法を使って、Reactアプリケーションに紙吹雪のアニメーションを簡単に追加できます。
必要に応じてカスタマイズしてみてください。

参考・引用

https://dev.classmethod.jp/articles/react-confetti-component/

https://zenn.dev/taigakiyokawa/articles/20221201-react-rewards-interval

https://qiita.com/yo-nagase/items/c09d926eb66b88c1aa4a

まさぴょんまさぴょん

紙吹雪、ダイアログ Sample Code🌟

次のような紙吹雪、ダイアログ Sample Code🌟

'use client'

import { useState } from 'react'
import { Check, X } from 'lucide-react'
import Confetti from 'react-confetti'
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import { Dialog, DialogContent } from "@/components/ui/dialog"

export default function Component() {
  const [rating, setRating] = useState(0)
  const [isOpen, setIsOpen] = useState(true)

  return (
    <Dialog open={isOpen} onOpenChange={setIsOpen}>
      <DialogContent className="max-w-sm p-0 bg-white">
        {isOpen && <Confetti 
          width={window.innerWidth}
          height={window.innerHeight}
          colors={['#ff6b6b', '#ff8787', '#ffa8a8']}
          numberOfPieces={200}
          gravity={0.2}
        />}
        <Card className="border-0 shadow-none">
          <CardContent className="p-6">
            <Button 
              variant="ghost" 
              size="icon" 
              className="absolute right-4 top-4" 
              onClick={() => setIsOpen(false)}
            >
              <X className="h-4 w-4" />
            </Button>
            
            <div className="flex flex-col items-center justify-center space-y-4">
              <div className="relative">
                <div className="w-20 h-20 bg-red-500 rounded-full flex items-center justify-center animate-scale-up">
                  <Check className="w-10 h-10 text-white" />
                </div>
              </div>

              <div className="text-center space-y-2">
                <h2 className="text-xl font-semibold">ご支援ありがとうございます!</h2>
                <p className="text-sm text-muted-foreground">チップを送信しました</p>
                <p className="text-2xl font-bold">¥1,350</p>
              </div>

              <div className="w-full pt-4 space-y-2">
                <p className="text-center text-sm">山田太郎さんの接客はいかがでしたか?</p>
                <div className="flex justify-center space-x-1">
                  {[1, 2, 3, 4, 5].map((star) => (
                    <Button
                      key={star}
                      variant="ghost"
                      size="icon"
                      className={`hover:bg-transparent ${rating >= star ? 'text-yellow-400' : 'text-gray-300'}`}
                      onClick={() => setRating(star)}
                    >
                      <svg
                        className="w-8 h-8"
                        fill="currentColor"
                        viewBox="0 0 20 20"
                      >
                        <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
                      </svg>
                    </Button>
                  ))}
                </div>
              </div>
            </div>
          </CardContent>
        </Card>
      </DialogContent>
    </Dialog>
  )
}