光的折射edit icon

创建者:
用户zdlDCoL7
Fork(复制)
下载
嵌入
BUG反馈
index.html
style.css
index.js
index.html
            
            <!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>光学折射原理交互演示</title>
    <style>
        body {
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            line-height: 1.6;
            color: #333;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        
        h1, h2 {
            color: #2c3e50;
            text-align: center;
        }
        
        .container {
            background-color: white;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            padding: 20px;
            margin-bottom: 20px;
        }
        
        .simulation-area {
            display: flex;
            flex-wrap: wrap;
            justify-content: space-between;
            margin-bottom: 30px;
        }
        
        .canvas-container {
            width: 60%;
            min-width: 400px;
        }
        
        .controls {
            width: 35%;
            min-width: 300px;
            padding: 15px;
            background-color: #f9f9f9;
            border-radius: 5px;
        }
        
        canvas {
            border: 1px solid #ddd;
            background-color: #e6f7ff;
            display: block;
            margin: 0 auto;
        }
        
        .control-group {
            margin-bottom: 15px;
        }
        
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        
        input[type="range"] {
            width: 100%;
        }
        
        .value-display {
            font-size: 14px;
            color: #666;
            margin-top: 5px;
        }
        
        .formula {
            background-color: #f0f8ff;
            padding: 15px;
            border-radius: 5px;
            margin: 20px 0;
            text-align: center;
            font-size: 18px;
            overflow-x: auto;
        }
        
        .interactive-tool {
            margin: 20px 0;
            padding: 15px;
            background-color: #f0f0f0;
            border-radius: 5px;
        }
        
        button {
            background-color: #3498db;
            color: white;
            border: none;
            padding: 8px 15px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.3s;
        }
        
        button:hover {
            background-color: #2980b9;
        }
        
        .quiz {
            margin: 20px 0;
            padding: 15px;
            background-color: #e8f4f8;
            border-radius: 5px;
        }
        
        .quiz-question {
            font-weight: bold;
            margin-bottom: 10px;
        }
        
        .quiz-option {
            margin: 5px 0;
            padding: 8px;
            background-color: white;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.2s;
        }
        
        .quiz-option:hover {
            background-color: #e1f5fe;
        }
        
        .feedback {
            margin-top: 10px;
            padding: 10px;
            border-radius: 4px;
            display: none;
        }
        
        .correct {
            background-color: #d4edda;
            color: #155724;
        }
        
        .incorrect {
            background-color: #f8d7da;
            color: #721c24;
        }
        
        .explanation {
            margin-top: 10px;
            font-size: 14px;
            color: #555;
        }
        
        @media (max-width: 768px) {
            .canvas-container, .controls {
                width: 100%;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>光学折射原理交互演示</h1>
        
        <div class="simulation-area">
            <div class="canvas-container">
                <canvas id="refractionCanvas" width="500" height="400"></canvas>
            </div>
            
            <div class="controls">
                <h2>参数调整</h2>
                
                <div class="control-group">
                    <label for="incidentAngle">入射角 (θ₁): <span id="incidentAngleValue">45</span>°</label>
                    <input type="range" id="incidentAngle" min="0" max="89" value="45" step="1">
                </div>
                
                <div class="control-group">
                    <label for="n1">介质1折射率 (n₁): <span id="n1Value">1.0</span></label>
                    <input type="range" id="n1" min="1" max="3" value="1.0" step="0.1">
                </div>
                
                <div class="control-group">
                    <label for="n2">介质2折射率 (n₂): <span id="n2Value">1.33</span></label>
                    <input type="range" id="n2" min="1" max="3" value="1.33" step="0.01">
                </div>
                
                <div class="control-group">
                    <label>折射角 (θ₂): <span id="refractionAngleValue">32.1</span>°</label>
                </div>
                
                <div class="control-group">
                    <button id="resetBtn">重置参数</button>
                    <button id="swapMediaBtn">交换介质</button>
                </div>
            </div>
        </div>
        
        <div class="formula">
            斯涅尔定律 (折射定律): 
            <strong>n₁·sin(θ₁) = n₂·sin(θ₂)</strong>
        </div>
        
        <div class="interactive-tool">
            <h2>折射角计算器</h2>
            <div class="control-group">
                <label for="calcN1">介质1折射率 (n₁):</label>
                <input type="number" id="calcN1" value="1.0" step="0.01" min="1" max="3">
            </div>
            <div class="control-group">
                <label for="calcN2">介质2折射率 (n₂):</label>
                <input type="number" id="calcN2" value="1.33" step="0.01" min="1" max="3">
            </div>
            <div class="control-group">
                <label for="calcAngle">入射角 (θ₁):</label>
                <input type="number" id="calcAngle" value="45" step="1" min="0" max="89">°
            </div>
            <button id="calculateBtn">计算折射角</button>
            <div id="calculationResult" class="value-display"></div>
        </div>
    </div>
    
    <div class="container">
        <h2>折射原理说明</h2>
        <p>当光从一种介质斜射入另一种介质时,传播方向会发生偏折,这种现象称为光的折射。</p>
        <p>折射现象遵循斯涅尔定律:n₁·sin(θ₁) = n₂·sin(θ₂),其中:</p>
        <ul>
            <li>n₁和n₂分别是两种介质的折射率</li>
            <li>θ₁是入射角(入射光线与法线的夹角)</li>
            <li>θ₂是折射角(折射光线与法线的夹角)</li>
        </ul>
        <p>折射率是描述介质对光的折射能力的物理量。真空的折射率为1,空气的折射率约为1.0003(通常近似为1),水的折射率约为1.33,玻璃的折射率约为1.5-1.7。</p>
        <p>当光从折射率较小的介质(如空气)进入折射率较大的介质(如水)时,折射角小于入射角;反之,折射角大于入射角。</p>
    </div>
    
    <div class="container">
        <h2>互动测验</h2>
        
        <div class="quiz">
            <div class="quiz-question">1. 当光线从空气(n=1.0)射入水(n=1.33)中时,如果入射角为30°,折射角会:</div>
            <div class="quiz-option" data-correct="true">小于30°</div>
            <div class="quiz-option">等于30°</div>
            <div class="quiz-option">大于30°</div>
            <div class="feedback"></div>
            <div class="explanation" style="display:none;">
                正确答案是"小于30°"。根据斯涅尔定律,当光从低折射率介质进入高折射率介质时,折射角小于入射角。
            </div>
        </div>
        
        <div class="quiz">
            <div class="quiz-question">2. 当入射角增大时,折射角会:</div>
            <div class="quiz-option" data-correct="true">也随之增大</div>
            <div class="quiz-option">减小</div>
            <div class="quiz-option">保持不变</div>
            <div class="feedback"></div>
            <div class="explanation" style="display:none;">
                正确答案是"也随之增大"。根据斯涅尔定律n₁·sin(θ₁) = n₂·sin(θ₂),当θ₁增大时,sin(θ₁)增大,因此sin(θ₂)也必须增大,意味着θ₂也增大。
            </div>
        </div>
        
        <div class="quiz">
            <div class="quiz-question">3. 当光线从水中射向空气时,如果入射角足够大,会发生什么现象?</div>
            <div class="quiz-option">折射角等于90°</div>
            <div class="quiz-option" data-correct="true">全反射</div>
            <div class="quiz-option">光线会沿界面传播</div>
            <div class="feedback"></div>
            <div class="explanation" style="display:none;">
                正确答案是"全反射"。当光从高折射率介质射向低折射率介质且入射角大于临界角时,会发生全反射现象,所有光都被反射回原介质。
            </div>
        </div>
    </div>
    
    <script>
        // 获取DOM元素
        const canvas = document.getElementById('refractionCanvas');
        const ctx = canvas.getContext('2d');
        
        // 控制元素
        const incidentAngleSlider = document.getElementById('incidentAngle');
        const n1Slider = document.getElementById('n1');
        const n2Slider = document.getElementById('n2');
        const incidentAngleValue = document.getElementById('incidentAngleValue');
        const n1Value = document.getElementById('n1Value');
        const n2Value = document.getElementById('n2Value');
        const refractionAngleValue = document.getElementById('refractionAngleValue');
        const resetBtn = document.getElementById('resetBtn');
        const swapMediaBtn = document.getElementById('swapMediaBtn');
        const calculateBtn = document.getElementById('calculateBtn');
        const calculationResult = document.getElementById('calculationResult');
        
        // 计算器输入
        const calcN1 = document.getElementById('calcN1');
        const calcN2 = document.getElementById('calcN2');
        const calcAngle = document.getElementById('calcAngle');
        
        // 初始化参数
        let incidentAngle = 45; // 入射角,单位度
        let n1 = 1.0;          // 介质1折射率
        let n2 = 1.33;         // 介质2折射率
        
        // 绘制折射演示
        function drawRefraction() {
            // 清除画布
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 设置坐标系原点在画布中心
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;
            
            // 绘制介质分界线
            ctx.beginPath();
            ctx.moveTo(0, centerY);
            ctx.lineTo(canvas.width, centerY);
            ctx.strokeStyle = '#000';
            ctx.lineWidth = 2;
            ctx.stroke();
            
            // 标注介质
            ctx.font = '16px Arial';
            ctx.fillStyle = '#333';
            ctx.fillText(`介质1 (n=${n1})`, 20, centerY - 20);
            ctx.fillText(`介质2 (n=${n2})`, 20, centerY + 40);
            
            // 计算折射角(弧度)
            const incidentAngleRad = incidentAngle * Math.PI / 180;
            let refractionAngleRad = Math.asin((n1 * Math.sin(incidentAngleRad)) / n2);
            
            // 检查是否全反射
            const criticalAngle = Math.asin(n2 / n1) * 180 / Math.PI;
            let isTotalReflection = false;
            
            if (n1 > n2 && incidentAngle >= criticalAngle) {
                isTotalReflection = true;
                refractionAngleRad = Math.PI / 2; // 设置为90度用于显示
            }
            
            // 转换为角度显示
            const refractionAngle = refractionAngleRad * 180 / Math.PI;
            refractionAngleValue.textContent = isTotalReflection ? "全反射" : refractionAngle.toFixed(1);
            
            // 绘制法线
            ctx.beginPath();
            ctx.moveTo(centerX, 0);
            ctx.lineTo(centerX, canvas.height);
            ctx.strokeStyle = '#888';
            ctx.setLineDash([5, 3]);
            ctx.stroke();
            ctx.setLineDash([]);
            
            // 绘制入射光线
            const rayLength = 200;
            const startX = centerX - rayLength * Math.sin(incidentAngleRad);
            const startY = centerY - rayLength * Math.cos(incidentAngleRad);
            
            ctx.beginPath();
            ctx.moveTo(startX, startY);
            ctx.lineTo(centerX, centerY);
            ctx.strokeStyle = '#e74c3c';
            ctx.lineWidth = 2;
            ctx.stroke();
            
            // 绘制折射光线(如果不是全反射)
            if (!isTotalReflection) {
                const endX = centerX + rayLength * Math.sin(refractionAngleRad);
                const endY = centerY + rayLength * Math.cos(refractionAngleRad);
                
                ctx.beginPath();
                ctx.moveTo(centerX, centerY);
                ctx.lineTo(endX, endY);
                ctx.strokeStyle = '#3498db';
                ctx.lineWidth = 2;
                ctx.stroke();
            } else {
                // 如果是全反射,绘制反射光线
                const endX = centerX - rayLength * Math.sin(incidentAngleRad);
                const endY = centerY - rayLength * Math.cos(incidentAngleRad);
                
                ctx.beginPath();
                ctx.moveTo(centerX, centerY);
                ctx.lineTo(endX, endY);
                ctx.strokeStyle = '#2ecc71';
                ctx.lineWidth = 2;
                ctx.setLineDash([5, 3]);
                ctx.stroke();
                ctx.setLineDash([]);
                
                // 显示全反射提示
                ctx.fillStyle = '#e67e22';
                ctx.fillText('全反射', centerX + 10, centerY - 20);
            }
            
            // 绘制角度标注
            drawAngleMarker(centerX, centerY, incidentAngleRad, true, '#e74c3c');
            if (!isTotalReflection) {
                drawAngleMarker(centerX, centerY, refractionAngleRad, false, '#3498db');
            }
            
            // 显示临界角信息(如果接近临界角)
            if (n1 > n2 && incidentAngle >= criticalAngle - 5 && incidentAngle <= criticalAngle + 5) {
                ctx.fillStyle = '#e67e22';
                ctx.fillText(`临界角: ${criticalAngle.toFixed(1)}°`, centerX - 100, 30);
            }
        }
        
        // 绘制角度标记
        function drawAngleMarker(x, y, angle, isIncident, color) {
            const radius = 30;
            const startAngle = isIncident ? -Math.PI/2 : Math.PI/2;
            const endAngle = isIncident ? startAngle - angle : startAngle + angle;
            
            ctx.beginPath();
            ctx.arc(x, y, radius, startAngle, endAngle, !isIncident);
            ctx.strokeStyle = color;
            ctx.lineWidth = 1;
            ctx.stroke();
            
            // 角度文本
            const angleDeg = (angle * 180 / Math.PI).toFixed(0);
            const textAngle = isIncident ? -angle/2 : angle/2;
            const textRadius = radius + 10;
            const textX = x + (isIncident ? -1 : 1) * textRadius * Math.sin(textAngle);
            const textY = y - textRadius * Math.cos(textAngle);
            
            ctx.fillStyle = color;
            ctx.font = '14px Arial';
            ctx.fillText(`${angleDeg}°`, textX - 10, textY + 5);
        }
        
        // 更新参数并重绘
        function updateParameters() {
            incidentAngle = parseFloat(incidentAngleSlider.value);
            n1 = parseFloat(n1Slider.value);
            n2 = parseFloat(n2Slider.value);
            
            incidentAngleValue.textContent = incidentAngle;
            n1Value.textContent = n1.toFixed(n1 % 1 === 0 ? 0 : 2);
            n2Value.textContent = n2.toFixed(n2 % 1 === 0 ? 0 : 2);
            
            drawRefraction();
        }
        
        // 重置参数
        function resetParameters() {
            incidentAngleSlider.value = 45;
            n1Slider.value = 1.0;
            n2Slider.value = 1.33;
            updateParameters();
        }
        
        // 交换介质
        function swapMedia() {
            const temp = n1Slider.value;
            n1Slider.value = n2Slider.value;
            n2Slider.value = temp;
            updateParameters();
        }
        
        // 计算折射角
        function calculateRefractionAngle() {
            const calcN1Val = parseFloat(calcN1.value);
            const calcN2Val = parseFloat(calcN2.value);
            const angle = parseFloat(calcAngle.value);
            
            if (isNaN(calcN1Val) || isNaN(calcN2Val) || isNaN(angle)) {
                calculationResult.textContent = "请输入有效数值!";
                calculationResult.style.color = "red";
                return;
            }
            
            if (angle < 0 || angle >= 90) {
                calculationResult.textContent = "入射角必须在0-89度之间!";
                calculationResult.style.color = "red";
                return;
            }
            
            const angleRad = angle * Math.PI / 180;
            const sinTheta2 = (calcN1Val * Math.sin(angleRad)) / calcN2Val;
            
            if (sinTheta2 > 1) {
                calculationResult.innerHTML = "计算结果: <strong>全反射</strong> (入射角大于临界角)";
                calculationResult.style.color = "#e67e22";
            } else {
                const theta2 = Math.asin(sinTheta2) * 180 / Math.PI;
                calculationResult.textContent = `折射角: ${theta2.toFixed(2)}°`;
                calculationResult.style.color = "#2c3e50";
            }
        }
        
        // 初始化测验交互
        function initQuiz() {
            const quizOptions = document.querySelectorAll('.quiz-option');
            
            quizOptions.forEach(option => {
                option.addEventListener('click', function() {
                    const isCorrect = this.getAttribute('data-correct') === 'true';
                    const feedback = this.parentElement.querySelector('.feedback');
                    const explanation = this.parentElement.querySelector('.explanation');
                    
                    feedback.textContent = isCorrect ? "✓ 正确!" : "✗ 错误";
                    feedback.className = 'feedback ' + (isCorrect ? 'correct' : 'incorrect');
                    feedback.style.display = 'block';
                    
                    explanation.style.display = 'block';
                    
                    // 禁用所有选项
                    quizOptions.forEach(opt => {
                        opt.style.pointerEvents = 'none';
                        if (opt.getAttribute('data-correct') === 'true') {
                            opt.style.backgroundColor = '#d4edda';
                        }
                    });
                });
            });
        }
        
        // 事件监听
        incidentAngleSlider.addEventListener('input', updateParameters);
        n1Slider.addEventListener('input', updateParameters);
        n2Slider.addEventListener('input', updateParameters);
        resetBtn.addEventListener('click', resetParameters);
        swapMediaBtn.addEventListener('click', swapMedia);
        calculateBtn.addEventListener('click', calculateRefractionAngle);
        
        // 初始化
        window.addEventListener('load', function() {
            updateParameters();
            initQuiz();
        });
    </script>
</body>
</html>
        
编辑器加载中
预览
控制台