Liquid Glass React実装ガイド:ウェブでのガラス効果実現
macOSTahoe ·
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リポジトリで確認してください。
