```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>🎉サウンドアニメーション🎶</title>
<style>
@keyframes bounce {
0% { transform: translateY(0); }
50% { transform: translateY(-20px); }
100% { transform: translateY(0); }
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes scaleUp {
0% { transform: scale(1); }
50% { transform: scale(1.5); }
100% { transform: scale(1); }
}
</style>
</head>
<body>
<div style="width:400px; height:400px; margin:0 auto; position:relative; background-color:#f0f8ff; overflow:hidden;">
<canvas id="emojiCanvas" width="400" height="400" style="background-color:transparent;"></canvas>
<button id="resetButton" style="position:absolute; bottom:10px; left:50%; transform:translateX(-50%); padding:10px 20px; font-size:16px; background-color:#ffeb3b; border:none; border-radius:5px; cursor:pointer;">
🔄 リセット
</button>
</div>
<script>
const canvas = document.getElementById('emojiCanvas');
const ctx = canvas.getContext('2d');
const resetButton = document.getElementById('resetButton');
let emojis = [];
let animationId;
// Emojiオブジェクトの定義
class Emoji {
constructor(char, x, y, size, color) {
this.char = char;
this.x = x;
this.y = y;
this.size = size;
this.color = color;
this.vx = (Math.random() - 0.5) * 2;
this.vy = (Math.random() - 0.5) * 2;
}
draw() {
ctx.font = `${this.size}px Arial`;
ctx.fillText(this.char, this.x, this.y);
}
update() {
this.x += this.vx;
this.y += this.vy;
// 壁から跳ね返る
if (this.x < 0 || this.x > canvas.width) this.vx *= -1;
if (this.y < 0 || this.y > canvas.height) this.vy *= -1;
}
}
// アニメーションループ
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
emojis.forEach(emoji => {
emoji.update();
emoji.draw();
});
animationId = requestAnimationFrame(animate);
}
// マイクから音を取得
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const analyser = audioContext.createAnalyser();
const source = audioContext.createMediaStreamSource(stream);
source.connect(analyser);
analyser.fftSize = 256;
const dataArray = new Uint8Array(analyser.frequencyBinCount);
function detectSound() {
analyser.getByteFrequencyData(dataArray);
let sum = dataArray.reduce((a, b) => a + b, 0);
let avg = sum / dataArray.length;
if (avg > 10) { // 小さな音でも反応
addEmoji();
}
requestAnimationFrame(detectSound);
}
detectSound();
})
.catch(err => {
alert('マイクへのアクセスが拒否されました。');
});
// 絵文字を追加
function addEmoji() {
const emojiList = ['🎉','😁','😂','😎','✨','🔥','💖','🐱🏍','🍀','🌟','🎈','🍉','🚀','🐶','🦄'];
const char = emojiList[Math.floor(Math.random() * emojiList.length)];
const size = Math.random() * 30 + 20;
const x = Math.random() * (canvas.width - size);
const y = Math.random() * (canvas.height - size);
const color = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6, '0');
emojis.push(new Emoji(char, x, y, size, color));
}
// リセットボタン
resetButton.addEventListener('click', () => {
emojis = [];
ctx.clearRect(0, 0, canvas.width, canvas.height);
cancelAnimationFrame(animationId);
animate();
});
// クリックでアニメーション
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const newEmoji = new Emoji('🎊', x, y, 40, '#ff5722');
emojis.push(newEmoji);
});
animate();
</script>
</body>
</html>
```