🌿
🌿 MidoriPhotoArt.
HTMLコンテンツを3D空間に表示。WebGLの3D空間内にSVGとCSSでスタイリングされた時計UIをOffscreenCanvasを使って描画し、それをテクスチャとして3Dプレーンに表示する機能

インタラクティブ時計UIシステム仕様書

1. システム概要

このシステムは、WebGL環境で動作するインタラクティブな時計UIを提供します。主な特徴は以下の通りです:

  • OffscreenCanvasを活用した動画背景付き時計表示
  • SVGベースの洗練された時計デザイン
  • WebGLテクスチャへのリアルタイム更新
  • アニメーション効果を含む高度なビジュアル表現

主要機能

  • 背景動画の再生と描画
  • 時刻と日付のリアルタイム表示
  • パルスアニメーション効果
  • 半透明ガラス調デザイン

システム構成図

OffscreenCanvas 背景動画描画 SVGテンプレート 時計UI生成 WebGLテクスチャ 3D表示

2. コアコンポーネント

初期化処理


// キャッシュ用のグローバル変数
let interactiveCanvas = null;

function initializeInteractiveCanvas(width = 1920, height = 1080) {
    if (!interactiveCanvas) {
        interactiveCanvas = new OffscreenCanvas(width, height);
        const context = interactiveCanvas.getContext('2d');

        // 背景動画要素の作成
        const video = document.createElement('video');
        video.src = 'images/052258d26dc78207dcd147a0204ed2a6fafcf98a68ced71930199c42ceda998d.mp4';
        video.autoplay = true;
        video.loop = true;
        video.muted = true;
        video.playsInline = true;

        // 共通のスタイル定義
        const commonStyles = `
            .clock-container {
                width: ${width}px;
                height: ${height}px;
                display: flex;
                justify-content: center;
                align-items: center;
                font-family: "Helvetica Neue", Arial, sans-serif;
                position: relative;
                overflow: hidden;
            }
            .clock-face {
                background: rgba(0, 0, 0, 0.5);
                border-radius: 50%;
                padding: 40px;
                backdrop-filter: blur(10px);
                border: 2px solid rgba(76, 175, 80, 0.3);
                box-shadow: 0 0 30px rgba(76, 175, 80, 0.2);
                position: relative;
                width: 400px;
                height: 400px;
                display: flex;
                justify-content: center;
                align-items: center;
            }
            .time {
                color: #4CAF50;
                font-size: 72px;
                font-weight: bold;
                text-shadow: 0 0 20px rgba(76, 175, 80, 0.5);
                letter-spacing: 2px;
            }
            .date {
                position: absolute;
                bottom: 80px;
                color: #e0e0e0;
                font-size: 24px;
                text-shadow: 0 0 10px rgba(76, 175, 80, 0.3);
            }
            @keyframes pulse {
                0% { transform: scale(1); opacity: 0.5; }
                50% { transform: scale(1.02); opacity: 0.8; }
                100% { transform: scale(1); opacity: 0.5; }
            }
            .clock-face::after {
                content: '';
                position: absolute;
                top: -2px;
                left: -2px;
                right: -2px;
                bottom: -2px;
                border-radius: 50%;
                background: linear-gradient(45deg, transparent, rgba(76, 175, 80, 0.3), transparent);
                animation: pulse 2s infinite;
                z-index: -1;
            }
        `;

        // 時計を更新する関数
        function updateClock() {
            const now = new Date();
            const time = now.toLocaleTimeString('ja-JP');
            const date = `${now.getFullYear()}年${String(now.getMonth() + 1).padStart(2, '0')}月${String(now.getDate()).padStart(2, '0')}日`;
            
            // 背景動画を描画
            context.drawImage(video, 0, 0, width, height);

            // SVGテンプレートを生成して描画
            const svgContent = generateSVGTemplate(time, date);
            const svgUrl = `data:image/svg+xml;charset=utf-8,${svgContent}`;
            
            const img = new Image();
            img.crossOrigin = 'anonymous';
            img.onload = () => {
                context.drawImage(img, 0, 0);
                if (interactiveUI) {
                    interactiveUI.material.map.needsUpdate = true;
                }
            };
            img.src = svgUrl;
        }

        // SVGテンプレート生成関数
        function generateSVGTemplate(time, date) {
            return encodeURIComponent(`
                <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
                    <foreignObject width="100%" height="100%">
                        <xhtml:div xmlns="http://www.w3.org/1999/xhtml">
                            <style>
                              ${commonStyles}
                            </style>
                            <div class="clock-container">
                                <div class="clock-face">
                                    <div id="time" class="time">${time}</div>
                                    <div id="date" class="date">${date}</div>
                                </div>
                            </div>                            
                        </xhtml:div>
                    </foreignObject>
                </svg>
            `);
        }

        // アニメーションループ内で更新を行う
        function animate() {
            requestAnimationFrame(animate);
            updateClock();
        }

        // ビデオの読み込みを待ってから初期化
        return new Promise((resolve, reject) => {
            video.onloadeddata = () => {
                video.play().then(() => {
                    // アニメーションループを開始
                    animate();
                    resolve(createInteractivePlane());
                }).catch(reject);
            };
            video.onerror = reject;
        });
    }

    // 2回目以降は既存のCanvasから直接プレーンを作成
    return Promise.resolve(createInteractivePlane());
}

スタイリング定義


.clock-container {
    /* ... 共通スタイル定義(上記参照)... */
}

.clock-face {
    /* ... 共通スタイル定義(上記参照)... */
}

.time {
    /* ... 共通スタイル定義(上記参照)... */
}

.date {
    /* ... 共通スタイル定義(上記参照)... */
}

@keyframes pulse {
    /* ... 共通スタイル定義(上記参照)... */
}

.clock-face::after {
    /* ... 共通スタイル定義(上記参照)... */
}

注意事項

  • OffscreenCanvasのブラウザ互換性に注意
  • 動画ファイルのサイズと形式の最適化が必要
  • WebGLコンテキストのメモリ管理に留意

3. インタラクティブUIの実装

インタラクティブな時計UIは、以下の特徴を持つ高度なシステムです:

  • OffscreenCanvasを使用した効率的なレンダリング
  • 動的な背景映像とブラー効果による美しいビジュアル
  • THREE.jsによる3D空間での表示
インタラクティブUI処理フロー

図3.1: インタラクティブUI処理の流れ

主な利点

  • パフォーマンスの最適化
    • OffscreenCanvasによるメインスレッドの負荷軽減
    • 効率的なメモリ使用とリソース管理
  • 視覚的な魅力
    • モダンで洗練されたデザイン
    • スムーズなアニメーションと遷移効果
  • 拡張性
    • 新機能の追加が容易
    • カスタマイズ可能なデザイン要素

コア機能の実装


// インタラクティブなUIプレーンを作成する関数
function createInteractivePlane() {
    const width = interactiveCanvas.width;
    const height = interactiveCanvas.height;
    
    const texture = new THREE.CanvasTexture(interactiveCanvas);
    texture.minFilter = THREE.LinearFilter;
    texture.magFilter = THREE.LinearFilter;

    const aspectRatio = width / height;
    const planeWidth = 150;
    const planeHeight = planeWidth / aspectRatio;

    const geometry = new THREE.PlaneGeometry(planeWidth, planeHeight);
    const material = new THREE.MeshBasicMaterial({
        map: texture,
        side: THREE.DoubleSide,
        transparent: true,
        opacity: 1.0
    });

    return new THREE.Mesh(geometry, material);
}

// シーン内のインタラクティブUIインスタンス
let interactiveUI = null;

async function setupInteractiveUI() {
    try {
        console.log('Starting to setup interactive UI...');
        
        if (!interactiveUI) {
            interactiveUI = await initializeInteractiveCanvas();
            interactiveUI.position.set(0, 0, 0);
            interactiveUI.scale.set(2, 2, 2);
        }
        
        scene.add(interactiveUI);
        console.log('Interactive UI setup completed');
    } catch (error) {
        console.error('Failed to setup interactive UI:', error);
    }
}

実装時の注意点:

  • ブラウザの互換性を必ず確認すること
  • メモリリークを防ぐため、適切なリソース解放を行うこと
  • パフォーマンスモニタリングを定期的に実施すること

4. 更新メカニズム

時計の更新は以下の手順で行われます:

  1. 背景動画のフレームを描画
  2. 現在時刻と日付を取得
  3. SVGテンプレートを生成
  4. テクスチャの更新
  5. アニメーションフレームの要求

更新処理の特徴:

  • requestAnimationFrameによる効率的なアニメーション
  • SVGとHTML要素の組み合わせによる柔軟なUI
  • 動的なスタイリングとアニメーション効果

更新処理の実装


// 時計を更新する関数 (initializeInteractiveCanvas内で定義済み)
function updateClock() {
    // ... 実装はinitializeInteractiveCanvas内のupdateClockを参照 ...
}

// アニメーションループ (initializeInteractiveCanvas内で定義済み)
function animate() {
    // ... 実装はinitializeInteractiveCanvas内のanimateを参照 ...
}

実装のポイント

  • OffscreenCanvasを使用した効率的な描画処理
  • SVGとCSSによる高品質なUI表現
  • requestAnimationFrameによる滑らかなアニメーション
  • テクスチャの動的更新による3D表示

5. WebGL統合

3D空間への統合は以下の手順で実現されます:

  • OffscreenCanvasを使用したテクスチャの生成
  • 動画背景とSVGクロックUIの合成
  • THREE.CanvasTextureによるテクスチャマッピング
  • PlaneGeometryへの適用とシーンへの統合

インタラクティブUI初期化


// インタラクティブUIの設定 (上記参照)
async function setupInteractiveUI() {
    // ... 実装は上記setupInteractiveUIを参照 ...
}

実装の重要ポイント

  • OffscreenCanvasによる効率的な描画処理
  • 動画とSVGの同期的な更新メカニズム
  • THREE.CanvasTextureの最適な設定
  • メモリ効率を考慮したテクスチャ管理

パフォーマンス最適化

  • テクスチャフィルタリングの適切な設定(LinearFilter使用)
  • 効率的なメモリ管理(単一のOffscreenCanvas再利用)
  • アニメーションフレームの最適な更新頻度設定
  • テクスチャのneedsUpdate制御による描画最適化