Harmonyos应用实例213:抛物线性质探索
·
5. 抛物线性质探索
功能简介:通过调整抛物线的焦点位置、准线,实时展示抛物线的标准方程、开口方向,支持观察参数变化对抛物线形状的影响。帮助学生理解抛物线的几何性质和定义。
ArkTS代码:
@Entry
@Component
struct ParabolaExplorer {
@State private p: number = 20 // 焦准距
@State private vertexX: number = 200 // 顶点X坐标
@State private vertexY: number = 200 // 顶点Y坐标
@State private parabolaDirection: string = 'right' // right, left, up, down
@State private equation: string = ''
@State private focusPosition: string = ''
@State private directrixEquation: string = ''
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
aboutToAppear() {
this.updateEquation()
}
build() {
Column() {
Text('🎯 抛物线性质探索')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
Canvas(this.context)
.width(400)
.height(400)
.backgroundColor('#f5f5f5')
.onReady(() => this.drawParabola())
// 参数调整区域
Column() {
// 开口方向选择
Row() {
Button('向右')
.fontSize(12)
.onClick(() => { this.parabolaDirection = 'right'; this.updateEquation() })
Button('向左')
.fontSize(12)
.onClick(() => { this.parabolaDirection = 'left'; this.updateEquation() })
Button('向上')
.fontSize(12)
.onClick(() => { this.parabolaDirection = 'up'; this.updateEquation() })
Button('向下')
.fontSize(12)
.onClick(() => { this.parabolaDirection = 'down'; this.updateEquation() })
}
.margin({ bottom: 15 })
// 焦准距调整
Row() {
Text('焦准距 p: ')
.width(100)
Slider({ value: this.p, min: 5, max: 50 })
.width(200)
.onChange((val: number) => {
this.p = val
this.updateEquation()
})
Text(this.p.toFixed(0))
.width(40)
}
.margin({ bottom: 10 })
// 顶点X坐标调整
Row() {
Text('顶点 X: ')
.width(100)
Slider({ value: this.vertexX, min: 50, max: 350 })
.width(200)
.onChange((val: number) => {
this.vertexX = val
this.updateEquation()
})
Text(this.vertexX.toFixed(0))
.width(40)
}
.margin({ bottom: 10 })
// 顶点Y坐标调整
Row() {
Text('顶点 Y: ')
.width(100)
Slider({ value: this.vertexY, min: 50, max: 350 })
.width(200)
.onChange((val: number) => {
this.vertexY = val
this.updateEquation()
})
Text(this.vertexY.toFixed(0))
.width(40)
}
}
.margin({ top: 20, bottom: 20 })
// 抛物线信息显示
Column() {
Text(this.equation)
.fontSize(16)
.fontColor('#2196F3')
.margin({ bottom: 10 })
Text(this.focusPosition)
.fontSize(14)
.fontColor('#FF5722')
.margin({ bottom: 10 })
Text(this.directrixEquation)
.fontSize(14)
.fontColor('#4CAF50')
.margin({ bottom: 10 })
Text('定义: 到焦点与准线距离相等的点的轨迹')
.fontSize(12)
.fontColor('#666')
}
.padding(15)
.backgroundColor('#f9f9f9')
.borderRadius(8)
.width('100%')
}
.padding(20)
.width('100%')
.height('100%')
}
private updateEquation() {
const a = this.p / 2 // 焦准距的一半
switch (this.parabolaDirection) {
case 'right':
this.equation = `标准方程: (y - ${this.vertexY.toFixed(0)})² = ${(2 * this.p).toFixed(0)}(x - ${this.vertexX.toFixed(0)})`
this.focusPosition = `焦点: (${(this.vertexX + a).toFixed(1)}, ${this.vertexY.toFixed(0)})`
this.directrixEquation = `准线: x = ${(this.vertexX - a).toFixed(1)}`
break
case 'left':
this.equation = `标准方程: (y - ${this.vertexY.toFixed(0)})² = -${(2 * this.p).toFixed(0)}(x - ${this.vertexX.toFixed(0)})`
this.focusPosition = `焦点: (${(this.vertexX - a).toFixed(1)}, ${this.vertexY.toFixed(0)})`
this.directrixEquation = `准线: x = ${(this.vertexX + a).toFixed(1)}`
break
case 'up':
this.equation = `标准方程: (x - ${this.vertexX.toFixed(0)})² = -${(2 * this.p).toFixed(0)}(y - ${this.vertexY.toFixed(0)})`
this.focusPosition = `焦点: (${this.vertexX.toFixed(0)}, ${(this.vertexY - a).toFixed(1)})`
this.directrixEquation = `准线: y = ${(this.vertexY + a).toFixed(1)}`
break
case 'down':
this.equation = `标准方程: (x - ${this.vertexX.toFixed(0)})² = ${(2 * this.p).toFixed(0)}(y - ${this.vertexY.toFixed(0)})`
this.focusPosition = `焦点: (${this.vertexX.toFixed(0)}, ${(this.vertexY + a).toFixed(1)})`
this.directrixEquation = `准线: y = ${(this.vertexY - a).toFixed(1)}`
break
}
this.drawParabola()
}
private drawParabola() {
const ctx = this.context
const width = 400
const height = 400
// 清空画布
ctx.clearRect(0, 0, width, height)
// 绘制坐标系
this.drawCoordinateSystem(ctx, width, height)
// 绘制抛物线
this.drawParabolaCurve(ctx)
// 绘制焦点
this.drawFocus(ctx)
// 绘制准线
this.drawDirectrix(ctx)
// 绘制顶点
this.drawVertex(ctx)
}
private drawCoordinateSystem(ctx: CanvasRenderingContext2D, width: number, height: number) {
ctx.strokeStyle = '#ccc'
ctx.lineWidth = 1
// 绘制网格
for (let i = 0; i <= width; i += 20) {
ctx.beginPath()
ctx.moveTo(i, 0)
ctx.lineTo(i, height)
ctx.stroke()
}
for (let i = 0; i <= height; i += 20) {
ctx.beginPath()
ctx.moveTo(0, i)
ctx.lineTo(width, i)
ctx.stroke()
}
// 绘制坐标轴
ctx.strokeStyle = '#333'
ctx.lineWidth = 2
// X轴
ctx.beginPath()
ctx.moveTo(0, height / 2)
ctx.lineTo(width, height / 2)
ctx.stroke()
// Y轴
ctx.beginPath()
ctx.moveTo(width / 2, 0)
ctx.lineTo(width / 2, height)
ctx.stroke()
// 绘制刻度
ctx.fillStyle = '#666'
ctx.font = '12px Arial'
ctx.textAlign = 'center'
for (let i = -10; i <= 10; i++) {
if (i === 0) continue
const x = width / 2 + i * 20
const y = height / 2
ctx.beginPath()
ctx.moveTo(x, y - 5)
ctx.lineTo(x, y + 5)
ctx.stroke()
ctx.fillText(i.toString(), x, y + 20)
}
for (let i = -10; i <= 10; i++) {
if (i === 0) continue
const x = width / 2
const y = height / 2 - i * 20
ctx.beginPath()
ctx.moveTo(x - 5, y)
ctx.lineTo(x + 5, y)
ctx.stroke()
ctx.fillText(i.toString(), x - 20, y + 4)
}
// 原点
ctx.fillText('O', width / 2 - 15, height / 2 + 15)
}
private drawParabolaCurve(ctx: CanvasRenderingContext2D) {
ctx.strokeStyle = '#2196F3'
ctx.lineWidth = 2
const a = this.p / 2
ctx.beginPath()
switch (this.parabolaDirection) {
case 'right':
// y² = 4ax (相对于顶点)
for (let y = -150; y <= 150; y += 1) {
const relativeY = y
const relativeX = (relativeY * relativeY) / (4 * a)
const screenX = this.vertexX + relativeX
const screenY = this.vertexY - relativeY
if (y === -150) {
ctx.moveTo(screenX, screenY)
} else {
ctx.lineTo(screenX, screenY)
}
}
break
case 'left':
// y² = -4ax (相对于顶点)
for (let y = -150; y <= 150; y += 1) {
const relativeY = y
const relativeX = -(relativeY * relativeY) / (4 * a)
const screenX = this.vertexX + relativeX
const screenY = this.vertexY - relativeY
if (y === -150) {
ctx.moveTo(screenX, screenY)
} else {
ctx.lineTo(screenX, screenY)
}
}
break
case 'up':
// x² = -4ay (相对于顶点)
for (let x = -150; x <= 150; x += 1) {
const relativeX = x
const relativeY = -(relativeX * relativeX) / (4 * a)
const screenX = this.vertexX + relativeX
const screenY = this.vertexY - relativeY
if (x === -150) {
ctx.moveTo(screenX, screenY)
} else {
ctx.lineTo(screenX, screenY)
}
}
break
case 'down':
// x² = 4ay (相对于顶点)
for (let x = -150; x <= 150; x += 1) {
const relativeX = x
const relativeY = (relativeX * relativeX) / (4 * a)
const screenX = this.vertexX + relativeX
const screenY = this.vertexY - relativeY
if (x === -150) {
ctx.moveTo(screenX, screenY)
} else {
ctx.lineTo(screenX, screenY)
}
}
break
}
ctx.stroke()
}
private drawFocus(ctx: CanvasRenderingContext2D) {
const a = this.p / 2
let focusX = this.vertexX
let focusY = this.vertexY
switch (this.parabolaDirection) {
case 'right':
focusX = this.vertexX + a
break
case 'left':
focusX = this.vertexX - a
break
case 'up':
focusY = this.vertexY - a
break
case 'down':
focusY = this.vertexY + a
break
}
// 绘制焦点
ctx.fillStyle = '#FF5722'
ctx.beginPath()
ctx.arc(focusX, focusY, 6, 0, 2 * Math.PI)
ctx.fill()
// 标注焦点
ctx.fillStyle = '#FF5722'
ctx.font = '12px Arial'
ctx.textAlign = 'left'
ctx.fillText('F', focusX + 8, focusY - 8)
}
private drawDirectrix(ctx: CanvasRenderingContext2D) {
const a = this.p / 2
ctx.strokeStyle = '#4CAF50'
ctx.lineWidth = 2
ctx.setLineDash([5, 5])
switch (this.parabolaDirection) {
case 'right': {
const directrixX = this.vertexX - a
ctx.beginPath()
ctx.moveTo(directrixX, 0)
ctx.lineTo(directrixX, 400)
ctx.stroke()
break
}
case 'left': {
const directrixX = this.vertexX + a
ctx.beginPath()
ctx.moveTo(directrixX, 0)
ctx.lineTo(directrixX, 400)
ctx.stroke()
break
}
case 'up': {
const directrixY = this.vertexY + a
ctx.beginPath()
ctx.moveTo(0, directrixY)
ctx.lineTo(400, directrixY)
ctx.stroke()
break
}
case 'down': {
const directrixY = this.vertexY - a
ctx.beginPath()
ctx.moveTo(0, directrixY)
ctx.lineTo(400, directrixY)
ctx.stroke()
break
}
}
ctx.setLineDash([])
// 标注准线
ctx.fillStyle = '#4CAF50'
ctx.font = '12px Arial'
ctx.textAlign = 'left'
switch (this.parabolaDirection) {
case 'right':
ctx.fillText('准线', this.vertexX - a + 5, 20)
break
case 'left':
ctx.fillText('准线', this.vertexX + a + 5, 20)
break
case 'up':
ctx.fillText('准线', 10, this.vertexY + a - 5)
break
case 'down':
ctx.fillText('准线', 10, this.vertexY - a - 5)
break
}
}
private drawVertex(ctx: CanvasRenderingContext2D) {
// 绘制顶点
ctx.fillStyle = '#9C27B0'
ctx.beginPath()
ctx.arc(this.vertexX, this.vertexY, 5, 0, 2 * Math.PI)
ctx.fill()
// 标注顶点
ctx.fillStyle = '#9C27B0'
ctx.font = '12px Arial'
ctx.textAlign = 'left'
ctx.fillText('V', this.vertexX + 8, this.vertexY + 20)
}
}
更多推荐


所有评论(0)