T-CREATOR

フェード・ズーム・スライド全部見せます!React で人気アニメーション完全網羅

フェード・ズーム・スライド全部見せます!React で人気アニメーション完全網羅

React でアニメーションを実装する際の基本概念と、フェード・ズーム・スライドの重要性について説明します。

現代の Web アプリケーションでは、ユーザーエクスペリエンスを向上させるためにアニメーションが欠かせません。特に React では、状態の変化を視覚的に表現することで、ユーザーに直感的な操作感を提供できます。

フェード、ズーム、スライドの 3 つのアニメーションは、Web サイトの約 80%で使用されていると言われています。これらの基本アニメーションをマスターすることで、プロフェッショナルな UI を構築できるようになります。

フェードアニメーションの実装

CSS Transitions を使ったフェードイン・アウト

最もシンプルで効率的なフェードアニメーションは、CSS Transitions を使用した実装です。React の状態管理と組み合わせることで、スムーズな表示・非表示の切り替えが可能になります。

css/* フェードアニメーション用のCSS */
.fade-enter {
  opacity: 0;
}

.fade-enter-active {
  opacity: 1;
  transition: opacity 300ms ease-in;
}

.fade-exit {
  opacity: 1;
}

.fade-exit-active {
  opacity: 0;
  transition: opacity 300ms ease-out;
}

この CSS クラスを React コンポーネントで使用する方法を見てみましょう。

jsximport React, { useState } from 'react';
import './FadeAnimation.css';

const FadeComponent = () => {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div>
      <button onClick={() => setIsVisible(!isVisible)}>
        {isVisible ? '非表示' : '表示'}
      </button>

      {isVisible && (
        <div className='fade-enter fade-enter-active'>
          フェードインするコンテンツ
        </div>
      )}
    </div>
  );
};

export default FadeComponent;

よくあるエラーとして、CSS クラスの適用タイミングが適切でない場合があります。以下のようなエラーが発生することがあります:

jsx// ❌ よくあるエラー例
const FadeComponent = () => {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div>
      {/* クラス名が間違っている例 */}
      <div className={isVisible ? 'fade-in' : 'fade-out'}>
        コンテンツ
      </div>
    </div>
  );
};

このエラーを解決するには、適切な CSS クラス名と状態管理が必要です。

React Transition Group による高度なフェード制御

より高度なフェードアニメーションを実装するには、React Transition Group を使用します。このライブラリは、コンポーネントのマウント・アンマウント時にアニメーションを制御できます。

まず、ライブラリをインストールします:

bashyarn add react-transition-group

次に、TransitionGroup を使用したフェードアニメーションを実装します:

jsximport React, { useState } from 'react';
import {
  CSSTransition,
  TransitionGroup,
} from 'react-transition-group';
import './FadeTransition.css';

const FadeTransitionComponent = () => {
  const [items, setItems] = useState([
    { id: 1, text: 'アイテム1' },
    { id: 2, text: 'アイテム2' },
  ]);

  const addItem = () => {
    const newId = items.length + 1;
    setItems([
      ...items,
      { id: newId, text: `アイテム${newId}` },
    ]);
  };

  const removeItem = (id) => {
    setItems(items.filter((item) => item.id !== id));
  };

  return (
    <div>
      <button onClick={addItem}>アイテム追加</button>

      <TransitionGroup>
        {items.map((item) => (
          <CSSTransition
            key={item.id}
            timeout={300}
            classNames='fade'
            unmountOnExit
          >
            <div className='item'>
              {item.text}
              <button onClick={() => removeItem(item.id)}>
                削除
              </button>
            </div>
          </CSSTransition>
        ))}
      </TransitionGroup>
    </div>
  );
};

export default FadeTransitionComponent;

対応する CSS ファイルを作成します:

css/* FadeTransition.css */
.fade-enter {
  opacity: 0;
  transform: translateY(20px);
}

.fade-enter-active {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 300ms, transform 300ms;
}

.fade-exit {
  opacity: 1;
  transform: translateY(0);
}

.fade-exit-active {
  opacity: 0;
  transform: translateY(-20px);
  transition: opacity 300ms, transform 300ms;
}

.item {
  padding: 10px;
  margin: 5px 0;
  background: #f0f0f0;
  border-radius: 4px;
}

React Transition Group でよく発生するエラーとその解決方法:

jsx// ❌ よくあるエラー:keyプロパティの不足
<TransitionGroup>
  {items.map(item => (
    <CSSTransition timeout={300} classNames="fade">
      {/* keyプロパティがないとエラーが発生 */}
      <div>{item.text}</div>
    </CSSTransition>
  ))}
</TransitionGroup>

// ✅ 正しい実装
<TransitionGroup>
  {items.map(item => (
    <CSSTransition
      key={item.id} // keyプロパティが必須
      timeout={300}
      classNames="fade"
    >
      <div>{item.text}</div>
    </CSSTransition>
  ))}
</TransitionGroup>

Framer Motion でのスムーズなフェード効果

Framer Motion は、宣言的なアニメーションライブラリで、より直感的にアニメーションを実装できます。複雑なアニメーションも簡単に作成できるのが特徴です。

まず、Framer Motion をインストールします:

bashyarn add framer-motion

基本的なフェードアニメーションの実装:

jsximport React, { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';

const FramerMotionFade = () => {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div>
      <button onClick={() => setIsVisible(!isVisible)}>
        {isVisible ? '非表示' : '表示'}
      </button>

      <AnimatePresence>
        {isVisible && (
          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -20 }}
            transition={{ duration: 0.3 }}
            style={{
              padding: '20px',
              background: '#f0f0f0',
              borderRadius: '8px',
              marginTop: '10px',
            }}
          >
            スムーズなフェードアニメーション
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
};

export default FramerMotionFade;

リストアイテムのフェードアニメーション:

jsximport React, { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';

const FramerMotionList = () => {
  const [items, setItems] = useState([
    { id: 1, text: 'アイテム1' },
    { id: 2, text: 'アイテム2' },
  ]);

  const addItem = () => {
    const newId =
      Math.max(...items.map((item) => item.id)) + 1;
    setItems([
      ...items,
      { id: newId, text: `アイテム${newId}` },
    ]);
  };

  const removeItem = (id) => {
    setItems(items.filter((item) => item.id !== id));
  };

  return (
    <div>
      <button onClick={addItem}>アイテム追加</button>

      <AnimatePresence>
        {items.map((item, index) => (
          <motion.div
            key={item.id}
            initial={{ opacity: 0, x: -50 }}
            animate={{ opacity: 1, x: 0 }}
            exit={{ opacity: 0, x: 50 }}
            transition={{
              duration: 0.3,
              delay: index * 0.1, // スタガードアニメーション
            }}
            style={{
              padding: '10px',
              margin: '5px 0',
              background: '#e0e0e0',
              borderRadius: '4px',
              display: 'flex',
              justifyContent: 'space-between',
              alignItems: 'center',
            }}
          >
            <span>{item.text}</span>
            <button onClick={() => removeItem(item.id)}>
              削除
            </button>
          </motion.div>
        ))}
      </AnimatePresence>
    </div>
  );
};

export default FramerMotionList;

Framer Motion でよく発生するエラー:

jsx// ❌ よくあるエラー:AnimatePresenceの使用方法
const FramerMotionError = () => {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div>
      {/* AnimatePresenceが条件分岐の外にあるとエラー */}
      <AnimatePresence>
        {isVisible && <motion.div>コンテンツ</motion.div>}
      </AnimatePresence>
    </div>
  );
};

// ✅ 正しい実装
const FramerMotionCorrect = () => {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div>
      <AnimatePresence mode='wait'>
        {isVisible && (
          <motion.div
            key='content'
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
          >
            コンテンツ
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
};

ズームアニメーションの実装

CSS Transform を使ったズームイン・アウト

ズームアニメーションは、要素のサイズを動的に変更することで実現します。CSS Transform の scale プロパティを使用することで、パフォーマンスの良いズーム効果を作成できます。

基本的なズームアニメーションの CSS:

css/* ズームアニメーション用のCSS */
.zoom-in {
  transform: scale(0);
  transition: transform 0.3s ease-out;
}

.zoom-in.active {
  transform: scale(1);
}

.zoom-out {
  transform: scale(1);
  transition: transform 0.3s ease-in;
}

.zoom-out.active {
  transform: scale(0);
}

.zoom-hover {
  transition: transform 0.2s ease;
}

.zoom-hover:hover {
  transform: scale(1.1);
}

React コンポーネントでの実装:

jsximport React, { useState, useEffect } from 'react';
import './ZoomAnimation.css';

const ZoomComponent = () => {
  const [isZoomed, setIsZoomed] = useState(false);

  useEffect(() => {
    // コンポーネントマウント時にズームイン
    const timer = setTimeout(() => setIsZoomed(true), 100);
    return () => clearTimeout(timer);
  }, []);

  return (
    <div>
      <div
        className={`zoom-in ${isZoomed ? 'active' : ''}`}
      >
        ズームインするコンテンツ
      </div>

      <button
        className='zoom-hover'
        onClick={() => setIsZoomed(!isZoomed)}
      >
        ズーム切り替え
      </button>
    </div>
  );
};

export default ZoomComponent;

ホバー効果でのズームアニメーション

ユーザーのインタラクションに応答するズームアニメーションは、UI の反応性を向上させます。

jsximport React from 'react';
import './HoverZoom.css';

const HoverZoomComponent = () => {
  return (
    <div className='hover-zoom-container'>
      <div className='zoom-card'>
        <h3>ホバーでズーム</h3>
        <p>マウスを乗せると拡大されます</p>
      </div>

      <div className='zoom-card'>
        <h3>クリックでズーム</h3>
        <p>クリックするとさらに拡大されます</p>
      </div>
    </div>
  );
};

export default HoverZoomComponent;

対応する CSS:

css/* HoverZoom.css */
.hover-zoom-container {
  display: flex;
  gap: 20px;
  padding: 20px;
}

.zoom-card {
  width: 200px;
  height: 150px;
  background: linear-gradient(
    135deg,
    #667eea 0%,
    #764ba2 100%
  );
  color: white;
  padding: 20px;
  border-radius: 12px;
  cursor: pointer;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.zoom-card:hover {
  transform: scale(1.05);
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
}

.zoom-card:active {
  transform: scale(0.95);
}

モーダル表示時のズーム効果

モーダルの表示・非表示時にズームアニメーションを使用することで、より自然な UI 体験を提供できます。

jsximport React, { useState } from 'react';
import './ModalZoom.css';

const ModalZoomComponent = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setIsModalOpen(true)}>
        モーダルを開く
      </button>

      {isModalOpen && (
        <div
          className='modal-overlay'
          onClick={() => setIsModalOpen(false)}
        >
          <div
            className='modal-content'
            onClick={(e) => e.stopPropagation()}
          >
            <h2>ズームアニメーション付きモーダル</h2>
            <p>このモーダルはズーム効果で表示されます</p>
            <button onClick={() => setIsModalOpen(false)}>
              閉じる
            </button>
          </div>
        </div>
      )}
    </div>
  );
};

export default ModalZoomComponent;

モーダル用の CSS:

css/* ModalZoom.css */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  animation: fadeIn 0.3s ease;
}

.modal-content {
  background: white;
  padding: 30px;
  border-radius: 12px;
  max-width: 500px;
  width: 90%;
  animation: zoomIn 0.3s ease;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes zoomIn {
  from {
    opacity: 0;
    transform: scale(0.3);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

スライドアニメーションの実装

横方向スライド(左右)

横方向のスライドアニメーションは、ナビゲーションやカルーセルでよく使用されます。

基本的な横スライドの CSS:

css/* 横スライドアニメーション用のCSS */
.slide-left {
  transform: translateX(100%);
  transition: transform 0.3s ease;
}

.slide-left.active {
  transform: translateX(0);
}

.slide-right {
  transform: translateX(-100%);
  transition: transform 0.3s ease;
}

.slide-right.active {
  transform: translateX(0);
}

.slide-container {
  overflow: hidden;
  position: relative;
}

React コンポーネントでの実装:

jsximport React, { useState } from 'react';
import './SlideAnimation.css';

const SlideComponent = () => {
  const [slideDirection, setSlideDirection] =
    useState('left');
  const [isVisible, setIsVisible] = useState(false);

  const handleSlide = (direction) => {
    setSlideDirection(direction);
    setIsVisible(true);
  };

  return (
    <div>
      <div className='slide-container'>
        <div
          className={`slide-${slideDirection} ${
            isVisible ? 'active' : ''
          }`}
        >
          スライドするコンテンツ
        </div>
      </div>

      <div style={{ marginTop: '20px' }}>
        <button onClick={() => handleSlide('left')}>
          左からスライド
        </button>
        <button onClick={() => handleSlide('right')}>
          右からスライド
        </button>
        <button onClick={() => setIsVisible(false)}>
          非表示
        </button>
      </div>
    </div>
  );
};

export default SlideComponent;

縦方向スライド(上下)

縦方向のスライドアニメーションは、アコーディオンやドロップダウンメニューで使用されます。

jsximport React, { useState } from 'react';
import './VerticalSlide.css';

const VerticalSlideComponent = () => {
  const [isExpanded, setIsExpanded] = useState(false);

  return (
    <div className='accordion'>
      <button
        className='accordion-header'
        onClick={() => setIsExpanded(!isExpanded)}
      >
        アコーディオン {isExpanded ? '▼' : '▶'}
      </button>

      <div
        className={`accordion-content ${
          isExpanded ? 'expanded' : ''
        }`}
      >
        <p>
          このコンテンツは縦方向にスライドして表示されます。
        </p>
        <p>
          アニメーションにより、スムーズな展開・収縮が可能です。
        </p>
      </div>
    </div>
  );
};

export default VerticalSlideComponent;

縦スライド用の CSS:

css/* VerticalSlide.css */
.accordion {
  border: 1px solid #ddd;
  border-radius: 8px;
  overflow: hidden;
  margin: 20px 0;
}

.accordion-header {
  width: 100%;
  padding: 15px;
  background: #f8f9fa;
  border: none;
  text-align: left;
  cursor: pointer;
  font-size: 16px;
  font-weight: 500;
}

.accordion-header:hover {
  background: #e9ecef;
}

.accordion-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease;
  background: white;
}

.accordion-content.expanded {
  max-height: 200px;
}

.accordion-content p {
  padding: 15px;
  margin: 0;
}

スライドショーの実装

複数の画像やコンテンツを順番に表示するスライドショーを実装します。

jsximport React, { useState, useEffect } from 'react';
import './Slideshow.css';

const SlideshowComponent = () => {
  const [currentSlide, setCurrentSlide] = useState(0);

  const slides = [
    { id: 1, content: 'スライド1', color: '#ff6b6b' },
    { id: 2, content: 'スライド2', color: '#4ecdc4' },
    { id: 3, content: 'スライド3', color: '#45b7d1' },
    { id: 4, content: 'スライド4', color: '#96ceb4' },
  ];

  useEffect(() => {
    const timer = setInterval(() => {
      setCurrentSlide((prev) => (prev + 1) % slides.length);
    }, 3000);

    return () => clearInterval(timer);
  }, [slides.length]);

  const goToSlide = (index) => {
    setCurrentSlide(index);
  };

  const nextSlide = () => {
    setCurrentSlide((prev) => (prev + 1) % slides.length);
  };

  const prevSlide = () => {
    setCurrentSlide(
      (prev) => (prev - 1 + slides.length) % slides.length
    );
  };

  return (
    <div className='slideshow-container'>
      <div className='slideshow'>
        {slides.map((slide, index) => (
          <div
            key={slide.id}
            className={`slide ${
              index === currentSlide ? 'active' : ''
            }`}
            style={{ backgroundColor: slide.color }}
          >
            <h2>{slide.content}</h2>
            <p>
              スライド {index + 1} / {slides.length}
            </p>
          </div>
        ))}
      </div>

      <button
        className='slideshow-btn prev'
        onClick={prevSlide}
      ></button>
      <button
        className='slideshow-btn next'
        onClick={nextSlide}
      ></button>

      <div className='slideshow-dots'>
        {slides.map((_, index) => (
          <button
            key={index}
            className={`dot ${
              index === currentSlide ? 'active' : ''
            }`}
            onClick={() => goToSlide(index)}
          />
        ))}
      </div>
    </div>
  );
};

export default SlideshowComponent;

スライドショー用の CSS:

css/* Slideshow.css */
.slideshow-container {
  position: relative;
  max-width: 600px;
  margin: 0 auto;
  overflow: hidden;
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}

.slideshow {
  position: relative;
  height: 300px;
}

.slide {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: white;
  opacity: 0;
  transform: translateX(100%);
  transition: all 0.5s ease;
}

.slide.active {
  opacity: 1;
  transform: translateX(0);
}

.slideshow-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: rgba(255, 255, 255, 0.8);
  border: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 20px;
  z-index: 10;
}

.slideshow-btn.prev {
  left: 10px;
}

.slideshow-btn.next {
  right: 10px;
}

.slideshow-dots {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 8px;
}

.dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.5);
  cursor: pointer;
  transition: background 0.3s ease;
}

.dot.active {
  background: white;
}

アニメーションの組み合わせ

フェード + ズームの複合効果

複数のアニメーションを組み合わせることで、より魅力的な効果を作成できます。

jsximport React, { useState } from 'react';
import './CombinedAnimation.css';

const CombinedAnimationComponent = () => {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div>
      <button onClick={() => setIsVisible(!isVisible)}>
        {isVisible ? '非表示' : '表示'}
      </button>

      <div
        className={`combined-animation ${
          isVisible ? 'active' : ''
        }`}
      >
        <h3>フェード + ズーム効果</h3>
        <p>
          このコンテンツはフェードインしながらズームインします
        </p>
      </div>
    </div>
  );
};

export default CombinedAnimationComponent;

複合アニメーション用の CSS:

css/* CombinedAnimation.css */
.combined-animation {
  opacity: 0;
  transform: scale(0.5);
  transition: opacity 0.5s ease, transform 0.5s ease;
  padding: 20px;
  background: linear-gradient(
    135deg,
    #667eea 0%,
    #764ba2 100%
  );
  color: white;
  border-radius: 12px;
  margin-top: 20px;
}

.combined-animation.active {
  opacity: 1;
  transform: scale(1);
}

スライド + フェードの連携

スライドとフェードを組み合わせたアニメーションは、ページ遷移やモーダル表示で効果的です。

jsximport React, { useState } from 'react';
import './SlideFadeAnimation.css';

const SlideFadeAnimationComponent = () => {
  const [isVisible, setIsVisible] = useState(false);
  const [direction, setDirection] = useState('right');

  const showContent = (dir) => {
    setDirection(dir);
    setIsVisible(true);
  };

  return (
    <div>
      <div className='button-group'>
        <button onClick={() => showContent('left')}>
          左からスライド + フェード
        </button>
        <button onClick={() => showContent('right')}>
          右からスライド + フェード
        </button>
        <button onClick={() => setIsVisible(false)}>
          非表示
        </button>
      </div>

      <div
        className={`slide-fade-container ${
          isVisible ? 'active' : ''
        }`}
      >
        <div
          className={`slide-fade-content slide-${direction}`}
        >
          <h3>スライド + フェード効果</h3>
          <p>
            このコンテンツは指定した方向からスライドしながらフェードインします
          </p>
        </div>
      </div>
    </div>
  );
};

export default SlideFadeAnimationComponent;

スライド + フェード用の CSS:

css/* SlideFadeAnimation.css */
.slide-fade-container {
  position: relative;
  height: 200px;
  overflow: hidden;
  margin-top: 20px;
}

.slide-fade-content {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(
    135deg,
    #ff6b6b 0%,
    #ee5a24 100%
  );
  color: white;
  padding: 20px;
  border-radius: 12px;
  opacity: 0;
  transform: translateX(100%);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

.slide-fade-content.slide-left {
  transform: translateX(-100%);
}

.slide-fade-content.slide-right {
  transform: translateX(100%);
}

.slide-fade-container.active .slide-fade-content {
  opacity: 1;
  transform: translateX(0);
}

.button-group {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

3 つのアニメーションを組み合わせた実例

フェード、ズーム、スライドをすべて組み合わせた高度なアニメーションを実装します。

jsximport React, { useState } from 'react';
import './TripleAnimation.css';

const TripleAnimationComponent = () => {
  const [isVisible, setIsVisible] = useState(false);
  const [animationType, setAnimationType] = useState(
    'fade-zoom-slide'
  );

  const animationTypes = [
    {
      id: 'fade-zoom-slide',
      name: 'フェード + ズーム + スライド',
    },
    {
      id: 'slide-fade-zoom',
      name: 'スライド + フェード + ズーム',
    },
    {
      id: 'zoom-slide-fade',
      name: 'ズーム + スライド + フェード',
    },
  ];

  return (
    <div>
      <div className='animation-controls'>
        <select
          value={animationType}
          onChange={(e) => setAnimationType(e.target.value)}
        >
          {animationTypes.map((type) => (
            <option key={type.id} value={type.id}>
              {type.name}
            </option>
          ))}
        </select>

        <button onClick={() => setIsVisible(!isVisible)}>
          {isVisible ? '非表示' : '表示'}
        </button>
      </div>

      <div
        className={`triple-animation ${animationType} ${
          isVisible ? 'active' : ''
        }`}
      >
        <h3>トリプルアニメーション効果</h3>
        <p>
          フェード、ズーム、スライドが組み合わさった美しいアニメーション
        </p>
        <div className='animation-indicator'>
          現在のアニメーション:{' '}
          {
            animationTypes.find(
              (t) => t.id === animationType
            )?.name
          }
        </div>
      </div>
    </div>
  );
};

export default TripleAnimationComponent;

トリプルアニメーション用の CSS:

css/* TripleAnimation.css */
.triple-animation {
  padding: 30px;
  background: linear-gradient(
    135deg,
    #667eea 0%,
    #764ba2 100%
  );
  color: white;
  border-radius: 16px;
  margin-top: 20px;
  opacity: 0;
  transform: scale(0.3) translateY(50px);
  transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.triple-animation.active {
  opacity: 1;
  transform: scale(1) translateY(0);
}

/* アニメーションタイプ別の調整 */
.triple-animation.fade-zoom-slide {
  transform: scale(0.3) translateY(50px);
}

.triple-animation.slide-fade-zoom {
  transform: scale(0.5) translateX(100px);
}

.triple-animation.zoom-slide-fade {
  transform: scale(0.2) translateX(-100px);
}

.animation-controls {
  display: flex;
  gap: 15px;
  align-items: center;
  margin-bottom: 20px;
}

.animation-controls select {
  padding: 8px 12px;
  border-radius: 6px;
  border: 1px solid #ddd;
}

.animation-indicator {
  margin-top: 15px;
  padding: 10px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 6px;
  font-size: 14px;
}

パフォーマンス最適化

アニメーションの最適化テクニック

アニメーションのパフォーマンスを最適化することで、スムーズな動作を実現できます。

jsximport React, { useState, useCallback } from 'react';
import './OptimizedAnimation.css';

const OptimizedAnimationComponent = () => {
  const [isVisible, setIsVisible] = useState(false);

  // パフォーマンス最適化のためのコールバック
  const handleToggle = useCallback(() => {
    setIsVisible((prev) => !prev);
  }, []);

  return (
    <div>
      <button onClick={handleToggle}>
        {isVisible ? '非表示' : '表示'}
      </button>

      {/* will-changeプロパティでGPU加速を有効化 */}
      <div
        className={`optimized-animation ${
          isVisible ? 'active' : ''
        }`}
        style={{
          willChange: isVisible
            ? 'transform, opacity'
            : 'auto',
        }}
      >
        <h3>最適化されたアニメーション</h3>
        <p>GPU加速と効率的なCSSプロパティを使用</p>
      </div>
    </div>
  );
};

export default OptimizedAnimationComponent;

最適化された CSS:

css/* OptimizedAnimation.css */
.optimized-animation {
  padding: 20px;
  background: linear-gradient(
    135deg,
    #4ecdc4 0%,
    #44a08d 100%
  );
  color: white;
  border-radius: 12px;
  margin-top: 20px;

  /* パフォーマンス最適化のためのプロパティ */
  opacity: 0;
  transform: translateY(30px) scale(0.9);
  transition: opacity 0.3s ease, transform 0.3s ease;

  /* GPU加速のための設定 */
  backface-visibility: hidden;
  perspective: 1000px;
}

.optimized-animation.active {
  opacity: 1;
  transform: translateY(0) scale(1);
}

/* アニメーション中のGPU加速 */
.optimized-animation.active {
  will-change: transform, opacity;
}

ブラウザ対応とフォールバック

異なるブラウザでの互換性を確保するためのフォールバック実装:

jsximport React, { useState, useEffect } from 'react';
import './BrowserSupport.css';

const BrowserSupportComponent = () => {
  const [supportsAnimation, setSupportsAnimation] =
    useState(true);
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    // ブラウザのアニメーションサポートをチェック
    const testAnimation = () => {
      const element = document.createElement('div');
      element.style.animation = 'test 1s';

      const hasAnimation = element.style.animation !== '';
      setSupportsAnimation(hasAnimation);
    };

    testAnimation();
  }, []);

  return (
    <div>
      <button onClick={() => setIsVisible(!isVisible)}>
        {isVisible ? '非表示' : '表示'}
      </button>

      <div
        className={`browser-support-animation ${
          isVisible ? 'active' : ''
        }`}
      >
        <h3>ブラウザ対応アニメーション</h3>
        <p>
          アニメーションサポート:{' '}
          {supportsAnimation ? '✅ 対応' : '❌ 非対応'}
        </p>
        {!supportsAnimation && (
          <p className='fallback-message'>
            このブラウザではアニメーションが無効化されています
          </p>
        )}
      </div>
    </div>
  );
};

export default BrowserSupportComponent;

ブラウザ対応用の CSS:

css/* BrowserSupport.css */
.browser-support-animation {
  padding: 20px;
  background: linear-gradient(
    135deg,
    #ff9a9e 0%,
    #fecfef 100%
  );
  border-radius: 12px;
  margin-top: 20px;

  /* 基本的な表示状態 */
  opacity: 0;
  transform: translateY(20px);
}

/* アニメーションサポートがある場合 */
@supports (animation: fadeIn 0.3s ease) {
  .browser-support-animation {
    transition: opacity 0.3s ease, transform 0.3s ease;
  }

  .browser-support-animation.active {
    opacity: 1;
    transform: translateY(0);
  }
}

/* アニメーションサポートがない場合のフォールバック */
@supports not (animation: fadeIn 0.3s ease) {
  .browser-support-animation.active {
    opacity: 1;
    transform: translateY(0);
  }
}

.fallback-message {
  background: #fff3cd;
  color: #856404;
  padding: 10px;
  border-radius: 6px;
  margin-top: 10px;
  border: 1px solid #ffeaa7;
}

/* ベンダープレフィックスの対応 */
.browser-support-animation {
  -webkit-transition: opacity 0.3s ease, -webkit-transform
      0.3s ease;
  -moz-transition: opacity 0.3s ease, -moz-transform 0.3s
      ease;
  -ms-transition: opacity 0.3s ease, -ms-transform 0.3s ease;
  transition: opacity 0.3s ease, transform 0.3s ease;
}

まとめ

React でのアニメーション実装について、フェード・ズーム・スライドの 3 つの基本アニメーションから、それらを組み合わせた高度な効果まで詳しく解説しました。

今回学んだ内容を振り返ると、アニメーションは単なる装飾ではなく、ユーザーエクスペリエンスを向上させる重要な要素であることがわかります。適切に実装されたアニメーションは、ユーザーに直感的な操作感を提供し、アプリケーションの品質を大幅に向上させます。

実装の際は、パフォーマンスを考慮し、ブラウザ対応も忘れずに行うことが重要です。CSS Transitions、React Transition Group、Framer Motion など、用途に応じて適切なライブラリを選択することで、効率的で美しいアニメーションを実現できます。

これらの技術を組み合わせることで、プロフェッショナルな Web アプリケーションを構築できるようになります。ぜひ実際のプロジェクトで活用してみてください。

関連リンク