アニメーションと音
コマの3D回転アニメーション、キラキラエフェクト、Web Audio APIによるサイン波/三角波でゲームに臨場感を追加。パフォーマンスとユーザー体験を両立します。
設計思想
アニメーションはCanvasの`requestAnimationFrame`代替として`setInterval`(60fps)を使用し、コマの反転を10フレームで完了。3D効果は`ctx.scale`でY軸圧縮、キラキラは放射グラデーションで表現。音はWeb Audio APIでサイン波(440Hz, 880Hz)、三角波(300Hz)、ノコギリ波/矩形波(結果表示)を生成。音量は0.08で控えめ、ロック機構で重複防止。
サンプルコード
function animateFlip(player) {
animationFrame = 0;
const interval = setInterval(() => {
animationFrame++;
drawBoard();
for (const piece of flippingPieces) {
const progress = Math.min(animationFrame / 10, 1);
piece.angle = progress * Math.PI;
piece.scale = 1 + 0.15 * Math.sin(progress * Math.PI);
ctx.save();
ctx.translate((piece.c + 0.5) * cellSize, (piece.r + 0.5) * cellSize);
ctx.scale(1, Math.cos(piece.angle));
ctx.rotate(piece.angle);
ctx.beginPath();
ctx.arc(0, 0, cellSize * 0.4 * piece.scale, 0, Math.PI * 2);
const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, cellSize * 0.4);
gradient.addColorStop(0, '#FFFFFF');
gradient.addColorStop(1, progress < 0.5 ? (player === 'black' ? 'white' : 'black') : player);
ctx.fillStyle = gradient;
ctx.fill();
ctx.restore();
}
if (animationFrame >= 10) {
clearInterval(interval);
flippingPieces.forEach(p => board[p.r][p.c] = player);
drawBoard();
playSound(300, 'triangle', 0.15);
gameState = 'playing';
flippingPieces = [];
}
}, 1000 / 60);
}
function playSound(frequency, type, duration) {
if (soundLock) return;
soundLock = true;
const oscillator = audioCtx.createOscillator();
oscillator.type = type;
oscillator.frequency.setValueAtTime(frequency, audioCtx.currentTime);
oscillator.connect(gainNode);
oscillator.start();
oscillator.stop(audioCtx.currentTime + duration);
setTimeout(() => soundLock = false, duration * 1000 + 50);
}
解説
`animateFlip`は各コマの回転角度(`angle`)とスケール(`scale`)を進捗(`progress`)に基づいて更新。`ctx.rotate`と`ctx.scale`で3D効果を、放射グラデーションでキラキラ感を演出。`playSound`は`AudioContext`でオシレーターを生成、音量を`gainNode`で制御。音の重複は`soundLock`で防止し、50msの余裕で安定性確保。アニメーションは約166msで完了し、パフォーマンスを最適化。