光的折射实验模拟器HTML代码

03 11月
作者:cinjep|分类:辅助教学

一个简单的光线折射模拟应用。该应用将允许用户调整入射角,并显示光线穿过玻璃砖后的折射路径。

物理原理:
斯涅尔定律(折射定律):n1 * sin(θ1) = n2 * sin(θ2)
其中,n1和n2分别是两种介质的折射率,θ1是入射角,θ2是折射角。

假设空气的折射率n1=1,玻璃的折射率n2=1.5。

光线从空气进入玻璃时,发生第一次折射(入射角θ1,折射角θ2),然后在玻璃中传播到另一面,再次从玻璃进入空气,发生第二次折射(此时入射角为θ2,折射角又变回θ1,因为对称性)。

光的折射实验模拟器_deepseek.PNG

以下是实现代码( 由 deepseek 依据人工指令生成):

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>光的折射实验模拟器</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Arial', sans-serif;
        }
        
        body {
            background-color: #f5f7fa;
            color: #333;
            line-height: 1.6;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background-color: white;
            border-radius: 12px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
            padding: 25px;
        }
        
        h1 {
            text-align: center;
            color: #2c3e50;
            margin-bottom: 20px;
        }
        
        .description {
            text-align: center;
            margin-bottom: 25px;
            color: #555;
        }
        
        .simulation-area {
            display: flex;
            flex-wrap: wrap;
            gap: 25px;
            margin-bottom: 25px;
        }
        
        .canvas-container {
            flex: 2;
            min-width: 600px;
            border: 1px solid #ddd;
            border-radius: 8px;
            overflow: hidden;
            background-color: #f9f9f9;
            position: relative;
        }
        
        #refractionCanvas {
            width: 100%;
            height: 500px;
            display: block;
        }
        
        .controls {
            flex: 1;
            min-width: 300px;
            padding: 20px;
            background-color: #f8f9fa;
            border-radius: 8px;
        }
        
        .control-group {
            margin-bottom: 20px;
        }
        
        h2 {
            color: #3498db;
            margin-bottom: 15px;
            font-size: 1.3rem;
        }
        
        label {
            display: block;
            margin-bottom: 8px;
            font-weight: bold;
            color: #555;
        }
        
        input[type="range"] {
            width: 100%;
            margin-bottom: 10px;
        }
        
        .value-display {
            display: flex;
            justify-content: space-between;
            font-size: 0.9rem;
            color: #666;
        }
        
        .info-panel {
            background-color: #e8f4fc;
            padding: 20px;
            border-radius: 8px;
            margin-bottom: 25px;
        }
        
        .info-panel h2 {
            color: #2980b9;
            margin-bottom: 15px;
        }
        
        .law-explanation {
            margin-bottom: 15px;
        }
        
        .formula {
            font-family: 'Courier New', monospace;
            background-color: #f1f1f1;
            padding: 8px 12px;
            border-radius: 4px;
            margin: 10px 0;
            display: inline-block;
        }
        
        .button {
            background-color: #3498db;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 1rem;
            transition: background-color 0.3s;
            margin-right: 10px;
            margin-bottom: 10px;
        }
        
        .button:hover {
            background-color: #2980b9;
        }
        
        .button.reset {
            background-color: #e74c3c;
        }
        
        .button.reset:hover {
            background-color: #c0392b;
        }
        
        .measurements {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin-top: 20px;
        }
        
        .measurement-box {
            background-color: white;
            padding: 15px;
            border-radius: 6px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.05);
            text-align: center;
        }
        
        .measurement-value {
            font-size: 1.5rem;
            font-weight: bold;
            color: #3498db;
            margin-top: 5px;
        }
        
        @media (max-width: 1024px) {
            .simulation-area {
                flex-direction: column;
            }
            
            .canvas-container, .controls {
                width: 100%;
            }
            
            .canvas-container {
                min-width: 100%;
            }
        }
    </style>
</head>
<body>
    <div>
        <h1>光的折射实验模拟器</h1>
        <p>通过调整参数观察光线通过玻璃砖时的折射现象,理解折射定律</p>
        
        <div>
            <div>
                <canvas id="refractionCanvas"></canvas>
            </div>
            
            <div>
                <div>
                    <h2>光线参数</h2>
                    
                    <label for="incidentAngle">入射角 (°)</label>
                    <input type="range" id="incidentAngle" min="0" max="80" value="30" step="1">
                    <div>
                        <span>0°</span>
                        <span id="incidentAngleValue">30°</span>
                        <span>80°</span>
                    </div>
                    
                    <label for="wavelength">光的波长 (nm)</label>
                    <input type="range" id="wavelength" min="380" max="780" value="550" step="10">
                    <div>
                        <span>380nm</span>
                        <span id="wavelengthValue">550nm</span>
                        <span>780nm</span>
                    </div>
                </div>
                
                <div>
                    <h2>介质参数</h2>
                    
                    <label for="glassRefractiveIndex">玻璃折射率</label>
                    <input type="range" id="glassRefractiveIndex" min="1.4" max="1.8" value="1.5" step="0.1">
                    <div>
                        <span>1.4</span>
                        <span id="glassRefractiveIndexValue">1.5</span>
                        <span>1.8</span>
                    </div>
                    
                    <label for="glassWidth">玻璃砖宽度</label>
                    <input type="range" id="glassWidth" min="150" max="400" value="250" step="10">
                    <div>
                        <span>窄</span>
                        <span id="glassWidthValue">中等</span>
                        <span>宽</span>
                    </div>
                    
                    <label for="glassThickness">玻璃砖厚度</label>
                    <input type="range" id="glassThickness" min="80" max="200" value="120" step="10">
                    <div>
                        <span>薄</span>
                        <span id="glassThicknessValue">中等</span>
                        <span>厚</span>
                    </div>
                </div>
                
                <div>
                    <h2>操作</h2>
                    <button id="toggleAnimation">暂停/播放动画</button>
                    <button class="button reset" id="resetButton">重置参数</button>
                </div>
            </div>
        </div>
        
        <div>
            <h2>折射定律 (斯涅尔定律)</h2>
            <div>
                <p>当光从一种介质进入另一种介质时,光的传播方向会发生改变,这种现象称为折射。</p>
                <p>n₁ × sin(θ₁) = n₂ × sin(θ₂)</p>
                <p>其中:</p>
                <ul>
                    <li>n₁ 和 n₂ 分别是两种介质的折射率</li>
                    <li>θ₁ 是入射角(入射光线与法线的夹角)</li>
                    <li>θ₂ 是折射角(折射光线与法线的夹角)</li>
                </ul>
            </div>
            
            <div>
                <div>
                    <div>入射角</div>
                    <div id="incidentAngleDisplay">30°</div>
                </div>
                <div>
                    <div>折射角</div>
                    <div id="refractionAngleDisplay">19.47°</div>
                </div>
                <div>
                    <div>出射角</div>
                    <div id="exitAngleDisplay">30°</div>
                </div>
                <div>
                    <div>侧向位移</div>
                    <div id="lateralDisplacementDisplay">16.1px</div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 获取DOM元素
        const canvas = document.getElementById('refractionCanvas');
        const ctx = canvas.getContext('2d');
        
        const incidentAngleSlider = document.getElementById('incidentAngle');
        const wavelengthSlider = document.getElementById('wavelength');
        const glassRefractiveIndexSlider = document.getElementById('glassRefractiveIndex');
        const glassWidthSlider = document.getElementById('glassWidth');
        const glassThicknessSlider = document.getElementById('glassThickness');
        
        const incidentAngleValue = document.getElementById('incidentAngleValue');
        const wavelengthValue = document.getElementById('wavelengthValue');
        const glassRefractiveIndexValue = document.getElementById('glassRefractiveIndexValue');
        const glassWidthValue = document.getElementById('glassWidthValue');
        const glassThicknessValue = document.getElementById('glassThicknessValue');
        
        const incidentAngleDisplay = document.getElementById('incidentAngleDisplay');
        const refractionAngleDisplay = document.getElementById('refractionAngleDisplay');
        const exitAngleDisplay = document.getElementById('exitAngleDisplay');
        const lateralDisplacementDisplay = document.getElementById('lateralDisplacementDisplay');
        
        const toggleAnimationButton = document.getElementById('toggleAnimation');
        const resetButton = document.getElementById('resetButton');
        
        // 初始化变量
        let incidentAngle = 30; // 入射角(度)
        let wavelength = 550; // 波长(nm)
        let glassRefractiveIndex = 1.5; // 玻璃折射率
        let glassWidth = 250; // 玻璃宽度(像素)
        let glassThickness = 120; // 玻璃厚度(像素)
        let airRefractiveIndex = 1.0; // 空气折射率
        let isAnimating = true;
        let animationFrameId;
        
        // 设置Canvas尺寸
        function resizeCanvas() {
            canvas.width = canvas.offsetWidth;
            canvas.height = canvas.offsetHeight;
            draw();
        }
        
        // 绘制场景
        function draw() {
            const width = canvas.width;
            const height = canvas.height;
            
            // 清除画布
            ctx.clearRect(0, 0, width, height);
            
            // 绘制背景
            ctx.fillStyle = '#e8f4fc';
            ctx.fillRect(0, 0, width, height);
            
            // 计算玻璃砖位置 - 向右偏移,为左侧留出空间
            const glassX = width * 0.5;
            const glassY = height / 2 - glassThickness / 2;
            
            // 绘制玻璃砖
            ctx.fillStyle = 'rgba(173, 216, 230, 0.5)';
            ctx.fillRect(glassX, glassY, glassWidth, glassThickness);
            ctx.strokeStyle = '#4682b4';
            ctx.lineWidth = 2;
            ctx.strokeRect(glassX, glassY, glassWidth, glassThickness);
            
            // 计算光线路径 - 从玻璃砖上方开始
            const rayStartX = glassX + glassWidth / 2;
            const rayStartY = glassY - 80; // 从玻璃砖上方80像素处开始
            
            // 入射光线
            const incidentAngleRad = incidentAngle * Math.PI / 180;
            
            // 计算入射点(玻璃上表面)
            const entryX = rayStartX + Math.tan(incidentAngleRad) * (glassY - rayStartY);
            const entryY = glassY;
            
            // 绘制入射光线
            drawRay(rayStartX, rayStartY, entryX, entryY, wavelengthToColor(wavelength));
            
            // 计算折射角
            const refractionAngleRad = Math.asin(
                (airRefractiveIndex * Math.sin(incidentAngleRad)) / glassRefractiveIndex
            );
            const refractionAngle = refractionAngleRad * 180 / Math.PI;
            
            // 玻璃内部光线
            const internalRayLength = glassThickness / Math.cos(refractionAngleRad);
            const internalRayEndX = entryX + Math.sin(refractionAngleRad) * internalRayLength;
            const internalRayEndY = entryY + Math.cos(refractionAngleRad) * internalRayLength;
            
            // 绘制玻璃内部光线
            drawRay(entryX, entryY, internalRayEndX, internalRayEndY, wavelengthToColor(wavelength), true);
            
            // 计算出射角(等于入射角)
            const exitAngleRad = Math.asin(
                (glassRefractiveIndex * Math.sin(refractionAngleRad)) / airRefractiveIndex
            );
            const exitAngle = exitAngleRad * 180 / Math.PI;
            
            // 出射光线
            const exitRayLength = 100;
            const exitRayEndX = internalRayEndX + Math.sin(exitAngleRad) * exitRayLength;
            const exitRayEndY = internalRayEndY + Math.cos(exitAngleRad) * exitRayLength;
            
            // 绘制出射光线
            drawRay(internalRayEndX, internalRayEndY, exitRayEndX, exitRayEndY, wavelengthToColor(wavelength));
            
            // 绘制法线(延长到玻璃砖上方和下方)
            drawExtendedNormal(entryX, entryY, glassThickness, rayStartY);
            drawExtendedNormal(internalRayEndX, internalRayEndY, glassThickness, rayStartY);
            
            // 绘制角度标注
            drawAngleMarkers(entryX, entryY, internalRayEndX, internalRayEndY, incidentAngle, refractionAngle, exitAngle);
            
            // 计算并显示侧向位移
            const lateralDisplacement = Math.abs(exitRayEndX - rayStartX);
            
            // 更新显示值
            incidentAngleDisplay.textContent = incidentAngle.toFixed(2) + '°';
            refractionAngleDisplay.textContent = refractionAngle.toFixed(2) + '°';
            exitAngleDisplay.textContent = exitAngle.toFixed(2) + '°';
            lateralDisplacementDisplay.textContent = lateralDisplacement.toFixed(1) + 'px';
        }
        
        // 绘制光线
        function drawRay(startX, startY, endX, endY, color, isDashed = false) {
            ctx.beginPath();
            ctx.moveTo(startX, startY);
            ctx.lineTo(endX, endY);
            ctx.strokeStyle = color;
            ctx.lineWidth = 2;
            
            if (isDashed) {
                ctx.setLineDash([5, 5]);
            } else {
                ctx.setLineDash([]);
            }
            
            ctx.stroke();
            ctx.setLineDash([]);
            
            // 绘制箭头
            drawArrow(endX, endY, Math.atan2(endY - startY, endX - startX));
        }
        
        // 绘制箭头
        function drawArrow(x, y, angle) {
            const arrowLength = 10;
            const arrowWidth = 5;
            
            ctx.save();
            ctx.translate(x, y);
            ctx.rotate(angle);
            
            ctx.beginPath();
            ctx.moveTo(0, 0);
            ctx.lineTo(-arrowLength, -arrowWidth);
            ctx.moveTo(0, 0);
            ctx.lineTo(-arrowLength, arrowWidth);
            ctx.stroke();
            
            ctx.restore();
        }
        
        // 绘制延长的法线
        function drawExtendedNormal(x, y, glassThickness, rayStartY) {
            const extendLength = 50; // 延长50像素
            
            ctx.beginPath();
            ctx.moveTo(x, y - extendLength); // 延长到玻璃砖上方
            ctx.lineTo(x, y + glassThickness + extendLength); // 延长到玻璃砖下方
            ctx.strokeStyle = '#888';
            ctx.lineWidth = 1;
            ctx.setLineDash([2, 2]);
            ctx.stroke();
            ctx.setLineDash([]);
        }
        
        // 绘制角度标注
        function drawAngleMarkers(entryX, entryY, exitX, exitY, incidentAngle, refractionAngle, exitAngle) {
            // 入射角文本
            ctx.fillStyle = '#e74c3c';
            ctx.font = 'bold 14px Arial';
            ctx.fillText(`入射角: ${incidentAngle.toFixed(1)}°`, entryX - 100, entryY - 15);
            
            // 折射角文本
            ctx.fillStyle = '#27ae60';
            ctx.fillText(`折射角: ${refractionAngle.toFixed(1)}°`, entryX - 100, entryY + 25);
            
            // 出射角文本
            ctx.fillStyle = '#2980b9';
            ctx.fillText(`出射角: ${exitAngle.toFixed(1)}°`, exitX - 100, exitY + 25);
        }
        
        // 将波长转换为颜色
        function wavelengthToColor(wavelength) {
            let r, g, b;
            
            if (wavelength >= 380 && wavelength < 440) {
                r = (-(wavelength - 440) / (440 - 380));
                g = 0.0;
                b = 1.0;
            } else if (wavelength >= 440 && wavelength < 490) {
                r = 0.0;
                g = ((wavelength - 440) / (490 - 440));
                b = 1.0;
            } else if (wavelength >= 490 && wavelength < 510) {
                r = 0.0;
                g = 1.0;
                b = (-(wavelength - 510) / (510 - 490));
            } else if (wavelength >= 510 && wavelength < 580) {
                r = ((wavelength - 510) / (580 - 510));
                g = 1.0;
                b = 0.0;
            } else if (wavelength >= 580 && wavelength < 645) {
                r = 1.0;
                g = (-(wavelength - 645) / (645 - 580));
                b = 0.0;
            } else if (wavelength >= 645 && wavelength <= 780) {
                r = 1.0;
                g = 0.0;
                b = 0.0;
            } else {
                r = 0.0;
                g = 0.0;
                b = 0.0;
            }
            
            // 强度调整
            let factor;
            if (wavelength >= 380 && wavelength < 420) {
                factor = 0.3 + 0.7 * (wavelength - 380) / (420 - 380);
            } else if (wavelength >= 420 && wavelength < 700) {
                factor = 1.0;
            } else if (wavelength >= 700 && wavelength <= 780) {
                factor = 0.3 + 0.7 * (780 - wavelength) / (780 - 700);
            } else {
                factor = 0.0;
            }
            
            r = Math.round(r * factor * 255);
            g = Math.round(g * factor * 255);
            b = Math.round(b * factor * 255);
            
            return `rgb(${r}, ${g}, ${b})`;
        }
        
        // 动画循环
        function animate() {
            if (isAnimating) {
                draw();
            }
            animationFrameId = requestAnimationFrame(animate);
        }
        
        // 事件监听器
        incidentAngleSlider.addEventListener('input', function() {
            incidentAngle = parseFloat(this.value);
            incidentAngleValue.textContent = incidentAngle + '°';
            draw();
        });
        
        wavelengthSlider.addEventListener('input', function() {
            wavelength = parseFloat(this.value);
            wavelengthValue.textContent = wavelength + 'nm';
            draw();
        });
        
        glassRefractiveIndexSlider.addEventListener('input', function() {
            glassRefractiveIndex = parseFloat(this.value);
            glassRefractiveIndexValue.textContent = glassRefractiveIndex.toFixed(1);
            draw();
        });
        
        glassWidthSlider.addEventListener('input', function() {
            glassWidth = parseFloat(this.value);
            const widthText = glassWidth < 200 ? '窄' : 
                            glassWidth < 300 ? '中等' : '宽';
            glassWidthValue.textContent = widthText;
            draw();
        });
        
        glassThicknessSlider.addEventListener('input', function() {
            glassThickness = parseFloat(this.value);
            const thicknessText = glassThickness < 100 ? '薄' : 
                             glassThickness < 150 ? '中等' : '厚';
            glassThicknessValue.textContent = thicknessText;
            draw();
        });
        
        toggleAnimationButton.addEventListener('click', function() {
            isAnimating = !isAnimating;
        });
        
        resetButton.addEventListener('click', function() {
            incidentAngleSlider.value = 30;
            wavelengthSlider.value = 550;
            glassRefractiveIndexSlider.value = 1.5;
            glassWidthSlider.value = 250;
            glassThicknessSlider.value = 120;
            
            incidentAngle = 30;
            wavelength = 550;
            glassRefractiveIndex = 1.5;
            glassWidth = 250;
            glassThickness = 120;
            
            incidentAngleValue.textContent = '30°';
            wavelengthValue.textContent = '550nm';
            glassRefractiveIndexValue.textContent = '1.5';
            glassWidthValue.textContent = '中等';
            glassThicknessValue.textContent = '中等';
            
            draw();
        });
        
        // 初始化
        window.addEventListener('resize', resizeCanvas);
        resizeCanvas();
        animate();
    </script>
</body>
</html>

光的折射实验模拟器_deepseek.html


浏览126
返回
目录
返回
首页
幼儿时钟学习应用设计 一元二次方程教学应用代码