Liquid Glass React実装ガイド:ウェブでのガラス効果実現
2025/06/19
7分で読む

Liquid Glass React実装ガイド:ウェブでのガラス効果実現

macOS TahoeのLiquid Glass UIをReactアプリケーションで再現する方法。CSS、JavaScript、パフォーマンス最適化の完全ガイド。

macOS TahoeのLiquid Glass UIの美しさをウェブアプリケーションで再現したいと思いませんか?この実装ガイドでは、ReactとCSS を使用してガラス効果を実現する方法を詳しく解説します。

基本概念の理解

Liquid Glassの核心要素

視覚的コンポーネント:

  • 透明度(Transparency): 背景の透過表示
  • ブラー(Blur): 背景のぼかし効果
  • 屈折(Refraction): 光の屈折シミュレーション
  • 反射(Reflection): 表面の反射効果
  • 動的応答(Dynamic Response): インタラクションへの反応

ウェブ実装の制約と可能性

ブラウザサポート:

  • backdrop-filter: Chrome 76+, Safari 9+, Firefox 103+
  • CSS filters: 全モダンブラウザ対応
  • CSS transforms: 全モダンブラウザ対応
  • CSS animations: 全モダンブラウザ対応

パフォーマンス考慮:

  • GPU加速の活用
  • レイヤー合成の最適化
  • 再描画の最小化
  • メモリ使用量の管理

基本的なガラス効果の実装

CSS基盤の構築

基本的なガラス効果CSS:

.liquid-glass {
  /* 基本的な透明度とブラー */
  background: rgba(255, 255, 255, 0.25);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  
  /* 境界線とシャドウ */
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
  
  /* 角の丸み */
  border-radius: 12px;
  
  /* GPU加速 */
  transform: translateZ(0);
  will-change: transform, opacity;
}
 
/* ダークモード対応 */
@media (prefers-color-scheme: dark) {
  .liquid-glass {
    background: rgba(0, 0, 0, 0.25);
    border: 1px solid rgba(255, 255, 255, 0.1);
    box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5);
  }
}

React基本コンポーネント

LiquidGlass基本コンポーネント:

import React from 'react';
import './LiquidGlass.css';
 
const LiquidGlass = ({ 
  children, 
  className = '', 
  intensity = 'medium',
  animated = false,
  ...props 
}) => {
  const getIntensityClass = (intensity) => {
    const intensityMap = {
      light: 'liquid-glass--light',
      medium: 'liquid-glass--medium',
      strong: 'liquid-glass--strong'
    };
    return intensityMap[intensity] || intensityMap.medium;
  };
 
  const classes = [
    'liquid-glass',
    getIntensityClass(intensity),
    animated ? 'liquid-glass--animated' : '',
    className
  ].filter(Boolean).join(' ');
 
  return (
    <div className={classes} {...props}>
      {children}
    </div>
  );
};
 
export default LiquidGlass;

強度別CSS設定:

/* 軽度の効果 */
.liquid-glass--light {
  background: rgba(255, 255, 255, 0.15);
  backdrop-filter: blur(10px);
  box-shadow: 0 4px 16px 0 rgba(31, 38, 135, 0.2);
}
 
/* 中程度の効果 */
.liquid-glass--medium {
  background: rgba(255, 255, 255, 0.25);
  backdrop-filter: blur(20px);
  box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
}
 
/* 強い効果 */
.liquid-glass--strong {
  background: rgba(255, 255, 255, 0.35);
  backdrop-filter: blur(30px);
  box-shadow: 0 12px 48px 0 rgba(31, 38, 135, 0.5);
}

高度な効果の実装

動的透明度とホバー効果

インタラクティブなガラス効果:

import React, { useState, useRef } from 'react';
 
const InteractiveLiquidGlass = ({ children, ...props }) => {
  const [isHovered, setIsHovered] = useState(false);
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
  const glassRef = useRef(null);
 
  const handleMouseMove = (e) => {
    if (glassRef.current) {
      const rect = glassRef.current.getBoundingClientRect();
      const x = ((e.clientX - rect.left) / rect.width) * 100;
      const y = ((e.clientY - rect.top) / rect.height) * 100;
      setMousePosition({ x, y });
    }
  };
 
  const dynamicStyle = {
    '--mouse-x': `${mousePosition.x}%`,
    '--mouse-y': `${mousePosition.y}%`,
    '--hover-intensity': isHovered ? '1' : '0'
  };
 
  return (
    <div
      ref={glassRef}
      className="liquid-glass liquid-glass--interactive"
      style={dynamicStyle}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
      onMouseMove={handleMouseMove}
      {...props}
    >
      {children}
    </div>
  );
};

対応するCSS:

.liquid-glass--interactive {
  position: relative;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
 
.liquid-glass--interactive::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: radial-gradient(
    circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
    rgba(255, 255, 255, calc(0.1 * var(--hover-intensity, 0))) 0%,
    transparent 50%
  );
  border-radius: inherit;
  pointer-events: none;
  opacity: var(--hover-intensity, 0);
  transition: opacity 0.3s ease;
}
 
.liquid-glass--interactive:hover {
  backdrop-filter: blur(25px);
  transform: translateY(-2px);
  box-shadow: 0 12px 40px 0 rgba(31, 38, 135, 0.4);
}

屈折効果のシミュレーション

CSS変形による屈折効果:

.liquid-glass--refraction {
  position: relative;
  overflow: hidden;
}
 
.liquid-glass--refraction::after {
  content: '';
  position: absolute;
  top: -50%;
  left: -50%;
  width: 200%;
  height: 200%;
  background: linear-gradient(
    45deg,
    transparent 30%,
    rgba(255, 255, 255, 0.1) 50%,
    transparent 70%
  );
  transform: rotate(-45deg);
  animation: refraction-sweep 3s ease-in-out infinite;
}
 
@keyframes refraction-sweep {
  0%, 100% {
    transform: translateX(-100%) rotate(-45deg);
  }
  50% {
    transform: translateX(100%) rotate(-45deg);
  }
}

複数レイヤーによる深度表現

多層ガラス効果コンポーネント:

const LayeredLiquidGlass = ({ children, layers = 3 }) => {
  const renderLayers = () => {
    return Array.from({ length: layers }, (_, index) => (
      <div
        key={index}
        className={`liquid-glass-layer liquid-glass-layer--${index + 1}`}
        style={{
          '--layer-index': index,
          '--total-layers': layers
        }}
      />
    ));
  };
 
  return (
    <div className="liquid-glass-container">
      {renderLayers()}
      <div className="liquid-glass-content">
        {children}
      </div>
    </div>
  );
};

レイヤー用CSS:

.liquid-glass-container {
  position: relative;
}
 
.liquid-glass-layer {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  border-radius: 12px;
  pointer-events: none;
}
 
.liquid-glass-layer--1 {
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(5px);
  transform: translateZ(calc(var(--layer-index) * 10px));
}
 
.liquid-glass-layer--2 {
  background: rgba(255, 255, 255, 0.15);
  backdrop-filter: blur(15px);
  transform: translateZ(calc(var(--layer-index) * 10px));
}
 
.liquid-glass-layer--3 {
  background: rgba(255, 255, 255, 0.2);
  backdrop-filter: blur(25px);
  transform: translateZ(calc(var(--layer-index) * 10px));
}
 
.liquid-glass-content {
  position: relative;
  z-index: 10;
}

パフォーマンス最適化

GPU加速の活用

最適化されたCSS:

.liquid-glass-optimized {
  /* GPU加速の強制 */
  transform: translateZ(0);
  will-change: transform, opacity, backdrop-filter;
  
  /* レイヤー合成の最適化 */
  isolation: isolate;
  
  /* 再描画の最小化 */
  contain: layout style paint;
}
 
/* アニメーション最適化 */
@keyframes optimized-hover {
  from {
    transform: translateZ(0) scale(1);
  }
  to {
    transform: translateZ(0) scale(1.02);
  }
}

React最適化戦略

メモ化とコールバック最適化:

import React, { memo, useCallback, useMemo } from 'react';
 
const OptimizedLiquidGlass = memo(({ 
  children, 
  intensity, 
  animated,
  onHover 
}) => {
  const glassStyle = useMemo(() => ({
    '--intensity': intensity,
    '--animation-duration': animated ? '0.3s' : '0s'
  }), [intensity, animated]);
 
  const handleHover = useCallback((e) => {
    if (onHover) {
      onHover(e);
    }
  }, [onHover]);
 
  return (
    <div 
      className="liquid-glass-optimized"
      style={glassStyle}
      onMouseEnter={handleHover}
    >
      {children}
    </div>
  );
});

条件付きレンダリング

パフォーマンス監視とフォールバック:

import { useState, useEffect } from 'react';
 
const usePerformanceMonitor = () => {
  const [isHighPerformance, setIsHighPerformance] = useState(true);
 
  useEffect(() => {
    // パフォーマンス監視
    const checkPerformance = () => {
      const connection = navigator.connection;
      const memory = navigator.deviceMemory;
      
      // 低スペックデバイスの検出
      if (connection?.effectiveType === '2g' || memory < 4) {
        setIsHighPerformance(false);
      }
    };
 
    checkPerformance();
  }, []);
 
  return isHighPerformance;
};
 
const AdaptiveLiquidGlass = ({ children, ...props }) => {
  const isHighPerformance = usePerformanceMonitor();
 
  if (!isHighPerformance) {
    // 低スペック向けフォールバック
    return (
      <div className="liquid-glass-fallback" {...props}>
        {children}
      </div>
    );
  }
 
  return (
    <LiquidGlass {...props}>
      {children}
    </LiquidGlass>
  );
};

実用的なコンポーネント例

ナビゲーションバー

ガラス効果ナビゲーション:

const LiquidGlassNavbar = ({ items, logo }) => {
  return (
    <nav className="liquid-glass-navbar">
      <LiquidGlass intensity="medium" className="navbar-container">
        <div className="navbar-content">
          <div className="navbar-logo">{logo}</div>
          <ul className="navbar-items">
            {items.map((item, index) => (
              <li key={index} className="navbar-item">
                <a href={item.href} className="navbar-link">
                  {item.label}
                </a>
              </li>
            ))}
          </ul>
        </div>
      </LiquidGlass>
    </nav>
  );
};

モーダルダイアログ

ガラス効果モーダル:

const LiquidGlassModal = ({ isOpen, onClose, children }) => {
  if (!isOpen) return null;
 
  return (
    <div className="modal-overlay" onClick={onClose}>
      <LiquidGlass 
        intensity="strong" 
        animated
        className="modal-content"
        onClick={(e) => e.stopPropagation()}
      >
        <button className="modal-close" onClick={onClose}>
          ×
        </button>
        {children}
      </LiquidGlass>
    </div>
  );
};

カードコンポーネント

インタラクティブカード:

const LiquidGlassCard = ({ title, content, image }) => {
  return (
    <InteractiveLiquidGlass className="glass-card">
      {image && (
        <div className="card-image">
          <img src={image} alt={title} />
        </div>
      )}
      <div className="card-content">
        <h3 className="card-title">{title}</h3>
        <p className="card-text">{content}</p>
      </div>
    </InteractiveLiquidGlass>
  );
};

ブラウザ互換性とフォールバック

機能検出

backdrop-filter サポート検出:

const supportsBackdropFilter = () => {
  return CSS.supports('backdrop-filter', 'blur(1px)') ||
         CSS.supports('-webkit-backdrop-filter', 'blur(1px)');
};
 
const LiquidGlassWithFallback = ({ children, ...props }) => {
  const [hasSupport, setHasSupport] = useState(false);
 
  useEffect(() => {
    setHasSupport(supportsBackdropFilter());
  }, []);
 
  return (
    <div 
      className={`
        liquid-glass 
        ${hasSupport ? 'liquid-glass--supported' : 'liquid-glass--fallback'}
      `}
      {...props}
    >
      {children}
    </div>
  );
};

フォールバックスタイル

非対応ブラウザ向けCSS:

.liquid-glass--fallback {
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid rgba(0, 0, 0, 0.1);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}
 
@supports not (backdrop-filter: blur(1px)) {
  .liquid-glass {
    background: rgba(255, 255, 255, 0.95);
  }
}

テストとデバッグ

パフォーマンステスト

レンダリングパフォーマンス測定:

const measureRenderPerformance = () => {
  const observer = new PerformanceObserver((list) => {
    const entries = list.getEntries();
    entries.forEach((entry) => {
      if (entry.entryType === 'measure') {
        console.log(`${entry.name}: ${entry.duration}ms`);
      }
    });
  });
 
  observer.observe({ entryTypes: ['measure'] });
 
  // 測定開始
  performance.mark('glass-render-start');
  
  // レンダリング後
  requestAnimationFrame(() => {
    performance.mark('glass-render-end');
    performance.measure(
      'glass-render-duration',
      'glass-render-start',
      'glass-render-end'
    );
  });
};

デバッグツール

開発者向けデバッグコンポーネント:

const LiquidGlassDebugger = ({ children, debug = false }) => {
  const [stats, setStats] = useState({});
 
  useEffect(() => {
    if (debug) {
      const updateStats = () => {
        setStats({
          backdropFilterSupport: supportsBackdropFilter(),
          devicePixelRatio: window.devicePixelRatio,
          memory: navigator.deviceMemory || 'unknown',
          connection: navigator.connection?.effectiveType || 'unknown'
        });
      };
 
      updateStats();
    }
  }, [debug]);
 
  return (
    <div className="liquid-glass-debug-container">
      <LiquidGlass>{children}</LiquidGlass>
      {debug && (
        <div className="debug-panel">
          <h4>Debug Info:</h4>
          <pre>{JSON.stringify(stats, null, 2)}</pre>
        </div>
      )}
    </div>
  );
};

結論

Liquid Glass効果のReact実装は、適切なCSS技術とパフォーマンス最適化により、ウェブアプリケーションでもmacOS Tahoeのような美しい体験を実現できます。

重要なポイント:

  • ブラウザサポート: backdrop-filterの対応状況を確認
  • パフォーマンス: GPU加速とメモリ使用量の最適化
  • アクセシビリティ: 動きの削減オプションの提供
  • フォールバック: 非対応環境での代替表示

この実装ガイドを参考に、美しく実用的なLiquid Glass UIをウェブアプリケーションで実現してください。


React Liquid Glassライブラリとサンプルコードについては、GitHubリポジトリで確認してください。

著者

avatar for macOSTahoe
macOSTahoe

カテゴリ

ニュースレター

コミュニティに参加

最新ニュースとアップデートをお届けするニュースレターに登録しましょう