【OpenHarmony/HarmonyOs 】Canvas 实现光的干涉:从波长滑块到动态条纹可视化
【OpenHarmony/HarmonyOs 】Canvas 实现光的干涉:从波长滑块到动态条纹可视化
本文基于我的 OpenHarmony/HarmonyOS 项目「物理视界 PhysicsVision」整理。项目中的「光的干涉」模型使用 ArkUI Canvas 绘制光源、双缝、波阵面、屏幕和干涉条纹,并通过滑块实时改变波长、缝间距和屏距。
这一篇单独拆解这个模型:如何把物理公式变成可交互的视觉体验。🌈
一、为什么要用 Canvas 做物理实验?
物理学习中有很多抽象概念,例如:
- 波长;
- 相位差;
- 光程差;
- 干涉加强和减弱;
- 条纹间距。
如果只用文字解释,学生很难形成直观印象。
而 Canvas 的价值就在于:可以把公式、参数和图像放在同一个交互循环里。
在「光的干涉」页面中,用户拖动滑块后会同时看到:
- 光源颜色变化;
- 干涉条纹颜色变化;
- 条纹间距变化;
- 数值面板实时刷新;
- 公式
Δy = λL/d对应结果变化。
这比静态图片更适合学习。
二、Canvas 初始化
页面中先创建 Canvas 渲染上下文:
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private cw: number = 360
private ch: number = 360
private canvasReady: boolean = false
在 UI 中使用 Canvas 组件:
Canvas(this.ctx)
.width('100%')
.height(340)
.backgroundColor('#1B2838')
.border({ width: 2, color: $r('app.color.border_strong') })
.borderRadius(20)
.onReady(() => {
this.canvasReady = true
this.drawScene()
})
onReady 很重要。只有 Canvas 准备好之后,才能安全调用绘制逻辑。
三、核心参数:波长、缝间距、屏距
光的干涉页面定义了三个核心状态:
@State slitSpacing: number = 0.5
@State wavelength: number = 550
@State screenDist: number = 2
分别对应:
slitSpacing:双缝间距,单位 mm;wavelength:波长,单位 nm;screenDist:屏幕距离,单位 m。
这三个变量共同决定条纹间距。
四、物理公式:条纹间距计算
双缝干涉条纹间距公式为:
Δy = λL / d
项目中的计算方法如下:
getFringeSpacing(): number {
return (this.wavelength * 1e-9 * this.screenDist) / (this.slitSpacing * 1e-3) * 1000
}
这里做了单位换算:
wavelength * 1e-9:nm 转 m;slitSpacing * 1e-3:mm 转 m;- 最后
* 1000:m 转 mm。
这个函数不只是用于显示数据,也会影响 Canvas 中条纹的视觉表现。
五、波长到颜色的映射
为了让光学实验更直观,项目把波长映射为颜色:
getColorFromWavelength(): string {
let wl: number = this.wavelength
if (wl < 440) return '#8B00FF'
if (wl < 490) return '#0000FF'
if (wl < 510) return '#00BFFF'
if (wl < 550) return '#00FF00'
if (wl < 590) return '#FFFF00'
if (wl < 630) return '#FF8C00'
return '#FF0000'
}
这不是严格的光谱渲染算法,但足够用于教学展示。
学生拖动波长滑块时,能直观看到颜色从紫、蓝、青、绿、黄、橙到红的变化。
六、绘制光源:颜色和光晕
在 drawScene 中,先绘制深色背景:
ctx.clearRect(0, 0, w, h)
ctx.fillStyle = '#1B2838'
ctx.fillRect(0, 0, w, h)
再绘制光源:
ctx.fillStyle = this.getColorFromWavelength()
ctx.beginPath()
ctx.arc(sourceX, cy, 10, 0, Math.PI * 2)
ctx.fill()
ctx.shadowColor = this.getColorFromWavelength()
ctx.shadowBlur = 15
ctx.fill()
ctx.shadowBlur = 0
shadowBlur 让光源产生发光感。
这类视觉细节虽然不是物理公式的一部分,但能增强沉浸感。
七、绘制双缝和光波
双缝前的挡板用矩形表示:
ctx.fillStyle = this.dimColor()
ctx.fillRect(slitX - 3, 0, 6, cy - 20)
ctx.fillRect(slitX - 3, cy - 10, 6, 20)
ctx.fillRect(slitX - 3, cy + 10, 6, h - cy - 10)
从双缝发出的波用圆弧表示:
ctx.globalAlpha = 0.15
for (let i = 1; i <= 8; i++) {
let r: number = i * 25
ctx.strokeStyle = this.getColorFromWavelength()
ctx.lineWidth = 1.5
ctx.beginPath()
ctx.arc(slitX, slitY1, r, -Math.PI / 3, Math.PI / 3)
ctx.stroke()
ctx.beginPath()
ctx.arc(slitX, slitY2, r, -Math.PI / 3, Math.PI / 3)
ctx.stroke()
}
ctx.globalAlpha = 1
这里用半透明圆弧表示波阵面,非常适合教学演示。
八、绘制干涉条纹
屏幕上的条纹根据强度计算透明度:
for (let y = -15; y <= 15; y++) {
let py: number = cy + y * fringeScale
if (py < 25 || py > h - 25) continue
let pathDiff: number = y * this.slitSpacing / this.screenDist
let phase: number = 2 * Math.PI * pathDiff / (this.wavelength * 0.001)
let intensity: number = Math.cos(phase / 2) * Math.cos(phase / 2)
ctx.fillStyle = color
ctx.globalAlpha = intensity * 0.9
ctx.fillRect(screenX - 8, py - fringeScale / 2.5, 16, fringeScale / 1.5)
}
ctx.globalAlpha = 1
这里的思路是:
- 用位置估算光程差;
- 用相位差估算干涉强度;
- 用透明度表示亮暗变化。
最终效果是:亮纹更亮,暗纹更淡。
九、滑块交互:参数变化后立即重绘
波长滑块:
Slider({ value: this.wavelength, min: 380, max: 700, step: 10 })
.trackColor($r('app.color.slider_track'))
.selectedColor(this.getColorFromWavelength())
.onChange((v: number) => {
this.wavelength = v
if (this.canvasReady) this.drawScene()
})
缝间距滑块:
Slider({ value: this.slitSpacing, min: 0.1, max: 1.0, step: 0.05 })
.selectedColor('#1A73E8')
.onChange((v: number) => {
this.slitSpacing = v
if (this.canvasReady) this.drawScene()
})
屏距滑块:
Slider({ value: this.screenDist, min: 0.5, max: 5, step: 0.1 })
.selectedColor('#1A73E8')
.onChange((v: number) => {
this.screenDist = v
if (this.canvasReady) this.drawScene()
})
每次参数变化都触发 drawScene,所以用户获得的是即时反馈。
十、数据面板:让图像和公式对应起来
页面下方展示干涉数据:
Text(`${this.getFringeSpacing().toFixed(2)} mm`)
.fontSize(17)
.fontWeight(FontWeight.Bolder)
.fontColor('#1A73E8')
Text('Δy = λL/d')
.fontSize(13)
.fontWeight(FontWeight.Bold)
.backgroundColor($r('app.color.bg_tag'))
这一步非常重要。
如果只有动画,学生可能只是觉得好看;加上公式和数值,才能真正把现象和知识连接起来。
总结
这个光的干涉模型展示了 ArkUI Canvas 在教育应用中的价值。
它不只是画图,而是把“参数输入、公式计算、物理图像、实时反馈”组合成一个完整学习场景。
对 OpenHarmony/HarmonyOS 开发者来说,这类页面非常适合做课程演示、实验模拟、科普应用和互动教学工具。
当学生拖动一个滑块就能看到光条纹变化时,物理公式就不再只是书上的符号了。🌈

更多推荐



所有评论(0)