インタラクティブ波形ラボ
インタラクティブな波のラボへようこそ!ここでは、伝播、回折、干渉という3つの重要な波動現象を学ぶことができます。
実験を選択してください:
波の伝播
波は光源から一定の速度であらゆる方向に広がります。
🌐 波の伝播
波は物質を運ぶことなくエネルギーを運びます。波源から同心円状に広がります。
- 伝播速度は媒体によって異なります
- 振幅が波の強度を決定します
- 伝播中、周波数は一定に保たれます
🌊 波の回折
回折とは、波が障害物を回り込んだり、狭い隙間を通り抜けたりする性質のことです。
- 障害物のサイズが波長に近いほど顕著になります
- 隙間を抜けた波は扇状に広がります
- 角を曲がって音が聞こえる理由を説明します
⚡ 波の干渉
干渉とは、2つのコヒーレントな波源からの波が重なり合う現象です(ヤングの二重スリット)。
- 明るい帯は強め合う干渉です
- 暗い帯は弱め合う干渉です
- 帯の間隔は波長によって決まります
📊 波形プロファイル
側面図は、空間を移動する正弦波の形を示しています。
- 振幅は平衡状態からの最大変位です
- 波長は周波数と速度に依存します
- 周波数が高いほど、波長は短くなります
🎯 実用例
🎵 音響学
コンサートホールの設計には音波の干渉が考慮されています
📡 無線通信
回折により、電波は山や建物を回り込むことができます
🔬 光学
光の干渉はレーザーやホログラフィーに利用されています

0.5) {
ctx.globalAlpha = intensity * (amplitude / 40);
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
}
ctx.globalAlpha = 1;
// Дифрагированные волны справа от щели
const slitSource = { x: barrierX, y: slitY };
for (let angle = -Math.PI/2; angle <= Math.PI/2; angle += Math.PI/20) {
const maxDist = canvas.width - barrierX;
for (let dist = 0; dist < maxDist; dist += 3) {
const x = slitSource.x + dist * Math.cos(angle);
const y = slitSource.y + dist * Math.sin(angle);
if (x >= barrierX && x < canvas.width && y >= 0 && y < canvas.height) {
const wavePhase = (dist / waveLength - time * frequency) * 2 * Math.PI;
const waveValue = Math.sin(wavePhase);
const distanceFactor = Math.max(0, 1 - dist / maxDist);
const angleFactor = Math.cos(angle) * Math.cos(angle);
if (waveValue > 0.3) {
const intensity = waveValue * distanceFactor * angleFactor * (amplitude / 40);
if (intensity > 0.1) {
ctx.fillStyle = 'rgba(255, 152, 0, ' + intensity + ')';
ctx.fillRect(x-1, y-1, 2, 2);
}
}
}
}
}
// Подписи (отодвинули от краев)
ctx.fillStyle = '#fff';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
ctx.fillText("\u30b9\u30ea\u30c3\u30c8", barrierX, Math.max(30, 20));
ctx.fillText("\u56de\u6298", Math.min(barrierX + 80, canvas.width - 50), Math.max(slitY - 40, 30));
}
// Рисование интерференции от двух щелей с экраном
function drawInterference() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const barrierX = canvas.width * 0.25;
const screenX = canvas.width * 0.6; // Экран ближе к щелям
const slitDistance = 50;
const centerY = canvas.height / 2;
const slit1Y = centerY - slitDistance/2;
const slit2Y = centerY + slitDistance/2;
const slitWidth = 6;
const waveSpeed = 50;
const waveLength = waveSpeed / frequency;
// Рисуем барьер с двумя щелями
ctx.fillStyle = '#444';
ctx.fillRect(barrierX - 3, 0, 6, slit1Y - slitWidth/2);
ctx.fillRect(barrierX - 3, slit1Y + slitWidth/2, 6, slit2Y - slitWidth/2 - (slit1Y + slitWidth/2));
ctx.fillRect(barrierX - 3, slit2Y + slitWidth/2, 6, canvas.height - (slit2Y + slitWidth/2));
// Падающие волны слева
ctx.strokeStyle = 'rgba(33, 150, 243, 0.6)';
ctx.lineWidth = 1;
for (let x = 0; x < barrierX; x += waveLength/3) {
const wavePhase = (x / waveLength - time * frequency) * 2 * Math.PI;
const intensity = Math.sin(wavePhase);
if (intensity > 0.5) {
ctx.globalAlpha = intensity * 0.5;
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
}
ctx.globalAlpha = 1;
// Источники в щелях
ctx.fillStyle = '#ff4444';
ctx.beginPath();
ctx.arc(barrierX, slit1Y, 3, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(barrierX, slit2Y, 3, 0, 2 * Math.PI);
ctx.fill();
// Анимированные волны от источников
ctx.strokeStyle = 'rgba(76, 175, 80, 0.3)';
ctx.lineWidth = 1;
for (let i = 0; i < 5; i++) {
const radius = (time * 30 + i * 25) % 120; // Уменьшили радиус
if (radius > 5) {
const alpha = Math.max(0, 0.3 - radius / 400);
ctx.globalAlpha = alpha;
// От первой щели
ctx.beginPath();
ctx.arc(barrierX, slit1Y, radius, 0, 2 * Math.PI);
ctx.stroke();
// От второй щели
ctx.beginPath();
ctx.arc(barrierX, slit2Y, radius, 0, 2 * Math.PI);
ctx.stroke();
}
}
ctx.globalAlpha = 1;
// Экран справа
ctx.fillStyle = '#222';
ctx.fillRect(screenX - 2, 0, 4, canvas.height);
// Статичная интерференционная картина на экране
const pattern = createInterferencePattern();
pattern.forEach(stripe => {
const { y, color, alpha, width } = stripe;
ctx.fillStyle = 'rgba(' + color[0] + ', ' + color[1] + ', ' + color[2] + ', ' + alpha + ')';
ctx.fillRect(screenX + 2, y, width, 1);
});
// Подписи (размещены подальше от краев)
ctx.fillStyle = '#fff';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
// Подписи щелей
const labelX = Math.max(barrierX - 25, 40);
ctx.fillText("\u30b9\u30ea\u30c3\u30c8 1", labelX, Math.max(slit1Y - 10, 20));
ctx.fillText("\u30b9\u30ea\u30c3\u30c8 2", labelX, Math.min(slit2Y + 20, canvas.height - 10));
// Подпись экрана
ctx.fillText("\u30b9\u30af\u30ea\u30fc\u30f3", screenX, Math.max(canvas.height - 15, canvas.height - 10));
// Подпись интерференции (размещена безопасно)
const interferenceX = Math.min(screenX + 30, canvas.width - 60);
const interferenceY = Math.max(35, 25);
ctx.font = '11px Arial';
ctx.fillText("\u5e72\u6e09", interferenceX, interferenceY);
}
// Рисование бокового вида волны
function drawSideView() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const centerY = canvas.height / 2;
const waveLength = 100 / frequency;
const maxAmplitude = Math.min(amplitude, canvas.height / 3);
// Ось X
ctx.strokeStyle = '#666';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(0, centerY);
ctx.lineTo(canvas.width, centerY);
ctx.stroke();
// Разметка оси
ctx.fillStyle = '#888';
ctx.font = '10px Arial';
ctx.textAlign = 'center';
for (let x = 0; x < canvas.width; x += waveLength) {
if (x > 20 && x < canvas.width - 20) { // Отступ от краев
ctx.beginPath();
ctx.moveTo(x, centerY - 5);
ctx.lineTo(x, centerY + 5);
ctx.stroke();
ctx.fillText('λ', x, centerY + 15);
}
}
// Волна
ctx.strokeStyle = '#4CAF50';
ctx.lineWidth = 3;
ctx.beginPath();
for (let x = 0; x < canvas.width; x += 2) {
const phase = (x / waveLength + time * frequency * 2) * 2 * Math.PI;
const y = centerY - maxAmplitude * Math.sin(phase);
if (x === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
// Стрелки амплитуды
ctx.strokeStyle = '#ff4444';
ctx.lineWidth = 2;
const arrowX = canvas.width * 0.25;
// Стрелка вверх
ctx.beginPath();
ctx.moveTo(arrowX, centerY);
ctx.lineTo(arrowX, centerY - maxAmplitude);
ctx.stroke();
// Стрелка вниз
ctx.beginPath();
ctx.moveTo(arrowX, centerY);
ctx.lineTo(arrowX, centerY + maxAmplitude);
ctx.stroke();
// Подписи (размещены с отступом от краев)
ctx.fillStyle = '#fff';
ctx.font = '14px Arial';
ctx.textAlign = 'left';
const labelY = Math.max(centerY - maxAmplitude - 10, 25);
ctx.fillText("\u632f\u5e45", Math.max(arrowX - 20, 20), labelY);
ctx.fillText('A = ' + amplitude, Math.max(arrowX - 20, 20), Math.max(labelY - 15, 15));
ctx.textAlign = 'center';
ctx.fillText("\u4f1d\u64ad\u65b9\u5411 \u2192", canvas.width/2, Math.max(40, 30));
ctx.fillText("\u5468\u6ce2\u6570 = " + frequency + " Hz", canvas.width/2, Math.min(canvas.height - 10, canvas.height - 20));
}
// Анимация
function animate() {
if (!isAnimating) return;
switch(currentExperiment) {
case 'propagation':
drawPropagation();
break;
case 'diffraction':
drawDiffraction();
break;
case 'interference':
drawInterference();
break;
case 'sideview':
drawSideView();
break;
}
time += 0.02;
animationFrame = requestAnimationFrame(animate);
}
// Управление экспериментами
window.switchExperiment = function(experiment) {
const buttons = document.querySelectorAll('.experiment-btn');
const cards = document.querySelectorAll('.theory-card');
const info = document.getElementById('experimentInfo');
buttons.forEach(btn => btn.classList.remove('active'));
cards.forEach(card => card.style.display = 'none');
currentExperiment = experiment;
time = 0;
const btnIds = {
'propagation': 'wavePropBtn',
'diffraction': 'diffractionBtn',
'interference': 'interferenceBtn',
'sideview': 'sideViewBtn'
};
const activeBtn = document.getElementById(btnIds[experiment]);
const activeCard = document.getElementById(experiment + 'Card');
if (activeBtn) activeBtn.classList.add('active');
if (activeCard) activeCard.style.display = 'block';
if (info) {
const titles = {
'propagation': "\u6ce2\u306e\u4f1d\u64ad",
'diffraction': "\u6ce2\u306e\u56de\u6298",
'interference': "\u6ce2\u306e\u5e72\u6e09",
'sideview': "\u6ce2\u5f62\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb"
};
const descriptions = {
'propagation': "\u6ce2\u306f\u6ce2\u6e90\u304b\u3089\u540c\u5fc3\u5186\u72b6\u306b\u5e83\u304c\u308a\u307e\u3059\u3002",
'diffraction': "\u6ce2\u306f\u30b9\u30ea\u30c3\u30c8\u3092\u901a\u308a\u3001\u6247\u72b6\u306b\u5e83\u304c\u308a\u307e\u3059\u3002",
'interference': "2\u3064\u306e\u30b9\u30ea\u30c3\u30c8\u304c\u30b9\u30af\u30ea\u30fc\u30f3\u4e0a\u306b\u9759\u6b62\u3057\u305f\u5e72\u6e09\u7e1e\u3092\u4f5c\u308a\u307e\u3059\u3002",
'sideview': "\u6b63\u5f26\u6ce2\u304c\u7a7a\u9593\u3092\u79fb\u52d5\u3057\u307e\u3059\u3002"
};
info.innerHTML = '
' + titles[experiment] + '
' + descriptions[experiment] + '
';
}
};
// Обновление параметров волн
window.updateWaveParams = function() {
const freqSlider = document.getElementById('frequencySlider');
const ampSlider = document.getElementById('amplitudeSlider');
const freqValue = document.getElementById('frequencyValue');
const ampValue = document.getElementById('amplitudeValue');
if (freqSlider && freqValue) {
frequency = parseFloat(freqSlider.value);
freqValue.textContent = frequency + " Hz";
}
if (ampSlider && ampValue) {
amplitude = parseFloat(ampSlider.value);
ampValue.textContent = amplitude;
}
// Пересчитать интерференционную картину при изменении параметров
interferencePattern = null;
};
// Управление анимацией
window.toggleWaveAnimation = function() {
const btn = document.getElementById('playPauseWaveBtn');
if (!btn) return;
if (isAnimating) {
isAnimating = false;
cancelAnimationFrame(animationFrame);
btn.textContent = "\u25b6\ufe0f \u518d\u751f";
} else {
isAnimating = true;
animate();
btn.textContent = "\u23f8\ufe0f \u4e00\u6642\u505c\u6b62";
}
};
window.resetWaveAnimation = function() {
time = 0;
if (!isAnimating) {
switch(currentExperiment) {
case 'propagation':
drawPropagation();
break;
case 'diffraction':
drawDiffraction();
break;
case 'interference':
drawInterference();
break;
case 'sideview':
drawSideView();
break;
}
}
};
// Запуск анимации
animate();
})()"
style="display: none;">