このシステムは、WebGL環境で動作するインタラクティブな時計UIを提供します。主な特徴は以下の通りです:
// キャッシュ用のグローバル変数
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 {
/* ... 共通スタイル定義(上記参照)... */
}
インタラクティブな時計UIは、以下の特徴を持つ高度なシステムです:
図3.1: インタラクティブUI処理の流れ
// インタラクティブな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);
}
}
実装時の注意点:
時計の更新は以下の手順で行われます:
更新処理の特徴:
// 時計を更新する関数 (initializeInteractiveCanvas内で定義済み)
function updateClock() {
// ... 実装はinitializeInteractiveCanvas内のupdateClockを参照 ...
}
// アニメーションループ (initializeInteractiveCanvas内で定義済み)
function animate() {
// ... 実装はinitializeInteractiveCanvas内のanimateを参照 ...
}
3D空間への統合は以下の手順で実現されます:
// インタラクティブUIの設定 (上記参照)
async function setupInteractiveUI() {
// ... 実装は上記setupInteractiveUIを参照 ...
}