news 2026/6/15 17:59:15

【无标题】用 HTML/CSS/JS 实现光的折射控制器:直观演示斯涅尔定律

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【无标题】用 HTML/CSS/JS 实现光的折射控制器:直观演示斯涅尔定律
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>光的折射控制器</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: "Microsoft Yahei", sans-serif; } body { padding: 20px; background-color: #f5f5f5; } .container { max-width: 800px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } h1 { text-align: center; color: #333; margin-bottom: 30px; } .controls { display: flex; flex-wrap: wrap; gap: 20px; margin-bottom: 30px; padding: 20px; background: #f8f8f8; border-radius: 8px; } .control-group { flex: 1; min-width: 200px; } label { display: block; margin-bottom: 8px; font-weight: bold; color: #555; } input[type="range"] { width: 100%; height: 6px; accent-color: #007bff; } input[type="color"] { width: 100%; height: 40px; border: none; cursor: pointer; } .value-display { margin-top: 5px; color: #666; font-size: 14px; } .canvas-container { position: relative; width: 100%; height: 400px; border: 1px solid #ddd; background: #fafafa; overflow: hidden; } #refractionCanvas { width: 100%; height: 100%; } .medium-label { position: absolute; font-weight: bold; color: #333; padding: 5px 10px; background: rgba(255,255,255,0.8); border-radius: 4px; } .medium1-label { top: 20px; left: 20px; } .medium2-label { bottom: 20px; left: 20px; } </style> </head> <body> <div class="container"> <h1>光的折射控制器</h1> <div class="controls"> <div class="control-group"> <label for="incidentAngle">入射角 (°)</label> <input type="range" id="incidentAngle" min="0" max="89" value="30"> <div class="value-display" id="incidentAngleValue">30°</div> </div> <div class="control-group"> <label for="n1">介质1 折射率</label> <input type="range" id="n1" min="1.0" max="2.0" step="0.01" value="1.0"> <div class="value-display" id="n1Value">1.0</div> </div> <div class="control-group"> <label for="n2">介质2 折射率</label> <input type="range" id="n2" min="1.0" max="2.0" step="0.01" value="1.5"> <div class="value-display" id="n2Value">1.5</div> </div> <div class="control-group"> <label for="lightColor">光线颜色</label> <input type="color" id="lightColor" value="#ff0000"> <div class="value-display" id="lightColorValue">红色 (#ff0000)</div> </div> </div> <div class="canvas-container"> <canvas id="refractionCanvas"></canvas> <div class="medium-label medium1-label">介质1</div> <div class="medium-label medium2-label">介质2</div> </div> </div> <script> // 获取DOM元素 const canvas = document.getElementById('refractionCanvas'); const ctx = canvas.getContext('2d'); const incidentAngleInput = document.getElementById('incidentAngle'); const n1Input = document.getElementById('n1'); const n2Input = document.getElementById('n2'); const lightColorInput = document.getElementById('lightColor'); const incidentAngleValue = document.getElementById('incidentAngleValue'); const n1Value = document.getElementById('n1Value'); const n2Value = document.getElementById('n2Value'); const lightColorValue = document.getElementById('lightColorValue'); // 初始化画布尺寸 function resizeCanvas() { const container = canvas.parentElement; canvas.width = container.offsetWidth; canvas.height = container.offsetHeight; drawRefraction(); } // 计算折射角(斯涅尔定律:n1*sin(θ1) = n2*sin(θ2)) function calculateRefractionAngle(incidentAngle, n1, n2) { const incidentRad = incidentAngle * Math.PI / 180; const sinIncident = Math.sin(incidentRad); const sinRefraction = (n1 * sinIncident) / n2; // 处理全反射 if (sinRefraction > 1) { return -1; // 全反射 } return Math.asin(sinRefraction) * 180 / Math.PI; } // 绘制折射效果 function drawRefraction() { // 获取参数 const incidentAngle = parseFloat(incidentAngleInput.value); const n1 = parseFloat(n1Input.value); const n2 = parseFloat(n2Input.value); const lightColor = lightColorInput.value; const refractionAngle = calculateRefractionAngle(incidentAngle, n1, n2); // 清空画布 ctx.clearRect(0, 0, canvas.width, canvas.height); // 画布中心和介质分界线 const centerX = canvas.width / 2; const divideY = canvas.height / 2; // 绘制介质分界线 ctx.beginPath(); ctx.moveTo(0, divideY); ctx.lineTo(canvas.width, divideY); ctx.strokeStyle = '#333'; ctx.lineWidth = 2; ctx.stroke(); // 绘制法线(虚线) ctx.beginPath(); ctx.moveTo(centerX, 0); ctx.lineTo(centerX, canvas.height); ctx.strokeStyle = '#888'; ctx.setLineDash([5, 5]); ctx.lineWidth = 1; ctx.stroke(); ctx.setLineDash([]); // 计算入射光线坐标 const incidentRad = incidentAngle * Math.PI / 180; const rayLength = Math.max(canvas.width, canvas.height) / 2; // 入射光线起点(左上) const incidentStartX = centerX - rayLength * Math.sin(incidentRad); const incidentStartY = divideY - rayLength * Math.cos(incidentRad); // 入射光线终点(交点) const incidentEndX = centerX; const incidentEndY = divideY; // 绘制入射光线 ctx.beginPath(); ctx.moveTo(incidentStartX, incidentStartY); ctx.lineTo(incidentEndX, incidentEndY); ctx.strokeStyle = lightColor; ctx.lineWidth = 3; ctx.stroke(); // 绘制入射光线箭头 drawArrow(ctx, incidentEndX, incidentEndY, incidentStartX, incidentStartY, lightColor); // 绘制折射/反射光线 if (refractionAngle === -1) { // 全反射 const reflectEndX = centerX + rayLength * Math.sin(incidentRad); const reflectEndY = divideY - rayLength * Math.cos(incidentRad); ctx.beginPath(); ctx.moveTo(incidentEndX, incidentEndY); ctx.lineTo(reflectEndX, reflectEndY); ctx.strokeStyle = lightColor; ctx.lineWidth = 3; ctx.stroke(); // 绘制反射箭头 drawArrow(ctx, incidentEndX, incidentEndY, reflectEndX, reflectEndY, lightColor); // 标注全反射 ctx.font = '16px Microsoft Yahei'; ctx.fillStyle = '#ff4444'; ctx.fillText('全反射', centerX + 50, divideY - 50); } else { // 折射光线 const refractionRad = refractionAngle * Math.PI / 180; const refractionEndX = centerX + rayLength * Math.sin(refractionRad); const refractionEndY = divideY + rayLength * Math.cos(refractionRad); ctx.beginPath(); ctx.moveTo(incidentEndX, incidentEndY); ctx.lineTo(refractionEndX, refractionEndY); ctx.strokeStyle = lightColor; ctx.lineWidth = 3; ctx.stroke(); // 绘制折射箭头 drawArrow(ctx, incidentEndX, incidentEndY, refractionEndX, refractionEndY, lightColor); } // 标注角度 ctx.font = '14px Microsoft Yahei'; ctx.fillStyle = '#333'; // 入射角标注 ctx.fillText(`入射角: ${incidentAngle}°`, centerX + 20, divideY - 20); // 折射角标注 if (refractionAngle !== -1) { ctx.fillText(`折射角: ${refractionAngle.toFixed(1)}°`, centerX + 20, divideY + 40); } // 折射率标注 ctx.fillText(`n1: ${n1}`, 20, 30); ctx.fillText(`n2: ${n2}`, 20, canvas.height - 10); } // 绘制箭头 function drawArrow(ctx, fromX, fromY, toX, toY, color) { const headLength = 10; // 箭头长度 const angle = Math.atan2(toY - fromY, toX - fromX); ctx.beginPath(); ctx.moveTo(toX, toY); ctx.lineTo( toX - headLength * Math.cos(angle - Math.PI / 6), toY - headLength * Math.sin(angle - Math.PI / 6) ); ctx.lineTo( toX - headLength * Math.cos(angle + Math.PI / 6), toY - headLength * Math.sin(angle + Math.PI / 6) ); ctx.closePath(); ctx.fillStyle = color; ctx.fill(); } // 更新数值显示 function updateDisplayValues() { incidentAngleValue.textContent = `${incidentAngleInput.value}°`; n1Value.textContent = `${n1Input.value}`; n2Value.textContent = `${n2Input.value}`; lightColorValue.textContent = `${getColorName(lightColorInput.value)} (${lightColorInput.value})`; } // 颜色值转名称(简化版) function getColorName(hex) { const colorMap = { '#ff0000': '红色', '#00ff00': '绿色', '#0000ff': '蓝色', '#ffff00': '黄色', '#ff00ff': '紫色', '#00ffff': '青色', '#000000': '黑色', '#ffffff': '白色' }; return colorMap[hex] || '自定义颜色'; } // 绑定事件 incidentAngleInput.addEventListener('input', () => { updateDisplayValues(); drawRefraction(); }); n1Input.addEventListener('input', () => { updateDisplayValues(); drawRefraction(); }); n2Input.addEventListener('input', () => { updateDisplayValues(); drawRefraction(); }); lightColorInput.addEventListener('input', () => { updateDisplayValues(); drawRefraction(); }); // 窗口大小变化时重绘 window.addEventListener('resize', resizeCanvas); // 初始化 resizeCanvas(); updateDisplayValues(); </script> </body> </html>

功能说明:

  1. 交互控件

    • 入射角调节(0°~89°):控制入射光线与法线的夹角
    • 介质 1/2 折射率调节(1.0~2.0):模拟不同介质(如空气、水、玻璃)
    • 光线颜色选择:自定义光线显示颜色
  2. 可视化效果

    • 清晰区分两种介质,显示介质分界线和法线(虚线)
    • 实时绘制入射光线和折射 / 反射光线,带箭头指示方向
    • 标注入射角、折射角(全反射时标注 “全反射”)
    • 自动处理全反射场景(当折射角计算结果超出范围时)
  3. 适配性

    • 响应式布局,适配不同屏幕尺寸
    • 基于 Canvas 绘制,保证光线效果流畅
    • 引用国内 CDN 的 jQuery(仅备用,核心逻辑未依赖)

使用方法:

  1. 将代码保存为.html文件(如refraction.html
  2. 用浏览器打开该文件即可使用
  3. 拖动滑块调节参数,颜色选择器更改光线颜色,实时查看折射效果
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 12:24:19

Airtest常用API介绍

今天就继续给大家介绍一下Airtest框架常用的核心API用法&#xff0c;这也是熟悉使用Airtest框架的必备手册之一。 (一&#xff09;官方文档 官方文档 https://airtest.doc.io.netease.com/IDEdocs/airtest_framework/1_airtest_api/ &#xff08;二&#xff09;核心API介绍…

作者头像 李华
网站建设 2026/6/15 13:22:48

国产突围与进口坚守:解读金相显微镜十大品牌新变局

金相显微镜是一种专门用于观察和分析金属及其合金微观结构的显微镜。它通过高倍放大的光学系统&#xff0c;帮助用户研究材料的金相组织、晶粒大小、相分布、缺陷&#xff08;如裂纹、气孔&#xff09;以及其它微观特征。目前行业内公认的国、内外一线/主流品牌大致集中在以下这…

作者头像 李华
网站建设 2026/6/15 12:39:59

医疗大模型LoRA微调实战:我用40行代码让AI学会看病

目录 &#x1f3af; 摘要 一、技术原理&#xff1a;为什么LoRA是医疗AI的救命稻草&#xff1f; 1.1 架构设计理念&#xff1a;别动基座&#xff0c;只加"外挂" 1.2 核心算法实现&#xff1a;矩阵拆解的魔法 1.3 性能特性分析&#xff1a;数据不说谎 二、实战部…

作者头像 李华
网站建设 2026/6/15 14:37:00

句句都不黄,[特殊字符]句句都很撩(贼上头)

&#x1f35a; 干饭要趁热&#xff0c;爱我要趁现在&#xff01;&#x1f964; 想喝奶茶三分糖&#xff0c;想你甜度超标啦&#xff5e;&#x1f6b6; 别问我去哪&#xff0c;心早就跑你那儿啦&#xff01;⏰ 闹钟叫不醒我&#xff0c;你一句想我就醒&#xff01;&#x1f4b0;…

作者头像 李华