【OpenHarmony/HarmonyOs 】凸透镜成像实验:光路绘制、成像公式与禁止 AI 识图的学习方案

本文基于我的 OpenHarmony/HarmonyOS 项目「物理视界 PhysicsVision」整理。项目中的「凸透镜成像」模型通过 Canvas 绘制主光轴、透镜、焦点、物体、像和两条典型光线,并根据物距和焦距实时计算成像类型。
这一篇对应“沉浸光感”和“禁止 AI 识图、隐私保护方案”的主题:不用拍照识题,也能把光学知识讲清楚。🔍

一、为什么凸透镜成像不一定需要拍照识题?

很多教育 App 会把光学题做成“拍照识别题目,然后给答案”。
但这种方式有几个问题:

  • 需要相机权限;
  • 图片可能包含学生个人信息;
  • 学生容易直接看答案;
  • 对理解成像规律帮助有限。

「物理视界」选择了另一种方式:
不拍照、不上传、不识图,而是让学生自己调节焦距、物距和物体高度,看成像结果如何变化。

这更适合物理学习,因为凸透镜成像的重点不是答案,而是规律。

二、核心参数

页面定义了三个输入参数:

@State focalLength: number = 60
@State objectDist: number = 150
@State objectHeight: number = 50

还有三个计算结果:

@State imageDist: number = 0
@State imageHeight: number = 0
@State imageType: string = ''

这对应物理中的:

  • 焦距 f
  • 物距 u
  • 像距 v
  • 放大率;
  • 成像性质。

三、成像公式计算

项目使用薄透镜公式:

calculate(): void {
  let u = this.objectDist
  let f = this.focalLength
  if (Math.abs(u - f) < 1) {
    this.imageDist = 9999
    this.imageHeight = 9999
    this.imageType = '平行光(不成像)'
    return
  }
  let v = (u * f) / (u - f)
  this.imageDist = v
  this.imageHeight = -v / u * this.objectHeight
}

公式本质是:

1/f = 1/u + 1/v

整理后得到:

v = uf / (u - f)

这里完全在端侧计算,不需要调用 AI 服务。

四、成像类型判断

根据物距和焦距关系,判断成像类型:

if (u > 2 * f) this.imageType = '倒立缩小实像'
else if (Math.abs(u - 2 * f) < 1) this.imageType = '倒立等大实像'
else if (u > f) this.imageType = '倒立放大实像'
else this.imageType = '正立放大虚像'

这正是初中光学常见知识点:

  • u > 2f:倒立缩小实像;
  • u = 2f:倒立等大实像;
  • f < u < 2f:倒立放大实像;
  • u < f:正立放大虚像。

通过代码,物理规律被明确地表达出来。

五、绘制主光轴和透镜

Canvas 中先绘制主光轴:

ctx.strokeStyle = '#B2BEC3'
ctx.lineWidth = 1
ctx.beginPath()
ctx.moveTo(10, centerY)
ctx.lineTo(w - 10, centerY)
ctx.stroke()

再绘制透镜:

ctx.strokeStyle = '#1A73E8'
ctx.lineWidth = 3
ctx.beginPath()
ctx.moveTo(centerX, centerY - 80)
ctx.lineTo(centerX, centerY + 80)
ctx.stroke()

蓝色透镜线条在浅色和深色背景下都比较清晰。

六、绘制焦点和 2F 点

let fPx = this.focalLength * scale
ctx.fillStyle = '#FF6D00'
ctx.beginPath()
ctx.arc(centerX - fPx, centerY, 4, 0, Math.PI * 2)
ctx.fill()
ctx.beginPath()
ctx.arc(centerX + fPx, centerY, 4, 0, Math.PI * 2)
ctx.fill()
ctx.fillText('F', centerX - fPx, centerY + 16)
ctx.fillText("F'", centerX + fPx, centerY + 16)

焦点和 2F 点是判断成像规律的关键参照。
把它们画在图上,学生能更容易理解“物体在 2F 外、F 和 2F 之间、F 内”的区别。

七、绘制物体和像

物体用红色箭头表示:

let objX = centerX - this.objectDist * scale
let objTop = centerY - this.objectHeight * 0.8
ctx.strokeStyle = '#F44336'
ctx.lineWidth = 3
ctx.beginPath()
ctx.moveTo(objX, centerY)
ctx.lineTo(objX, objTop)
ctx.stroke()

像用绿色箭头表示:

let imgX = centerX + this.imageDist * scale
let imgH = this.imageHeight * 0.8
let imgTop = centerY - imgH
let isVirtual = this.imageDist < 0
if (isVirtual) {
  ctx.setLineDash([5, 5])
}
ctx.strokeStyle = '#4CAF50'
ctx.beginPath()
ctx.moveTo(imgX, centerY)
ctx.lineTo(imgX, imgTop)
ctx.stroke()
ctx.setLineDash([])

虚像使用虚线,这是一个很适合教学的视觉约定。

八、绘制两条典型光线

项目绘制了平行光线和过光心光线:

ctx.strokeStyle = '#FFB74D'
ctx.lineWidth = 1

ctx.beginPath()
ctx.moveTo(objX, objTop)
ctx.lineTo(centerX, objTop)
ctx.lineTo(imgX, imgTop)
ctx.stroke()

ctx.beginPath()
ctx.moveTo(objX, objTop)
ctx.lineTo(imgX, imgTop)
ctx.stroke()

这比只显示像的位置更好,因为学生能看到成像是如何由光路决定的。

九、滑块调参:主动探索规律

焦距、物距和物体高度都用 Slider 控制:

Slider({ value: this.objectDist, min: 20, max: 250, step: 1 })
  .trackColor($r('app.color.slider_track'))
  .selectedColor('#1A73E8')
  .onChange((v: number) => {
    this.objectDist = v
    this.calculate()
  })

每次滑动都会重新计算并绘图。
这让学生可以自己观察:

  • 物距变小时像距怎么变;
  • 什么时候不成像;
  • 实像和虚像如何切换;
  • 像的大小如何变化。

十、总结

凸透镜成像页面很好地体现了“禁止 AI 识图”的另一种学习方案。
不拍照、不上传、不识别题目,而是让学生通过参数调节和光路观察理解规律。

这篇文章对应的主题是:沉浸光感 + 禁止 AI 识图 + 隐私友好学习
对 OpenHarmony/HarmonyOS 开发者来说,它也展示了 Canvas 在光学教学中的实用价值。🔍

img

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐