Harmonyos应用实例208: 圆锥曲线统一演化
·
10. 圆锥曲线统一演化
对应章节:第三章 综合复习
功能简介:
通过一个参数(离心率 eee)的变化,展示圆锥曲线的统一性。
- 0<e<10 < e < 10<e<1:椭圆
- e=1e = 1e=1:抛物线
- e>1e > 1e>1:双曲线
屏幕上有一个动态变化的曲线,随着滑块从0滑动到2,曲线形状连续变化,完美体现圆锥曲线的统一定义(第二定义)。
@Entry
@Component
struct ConicUnification {
@State e: number = 0.5 // 离心率 e
@State p: number = 50 // 焦准距 p
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
build() {
Column() {
Text('圆锥曲线统一演示')
.fontSize(24).fontWeight(FontWeight.Bold).margin({ bottom: 20 })
Text(`离心率 e = ${this.e.toFixed(2)}`)
.fontSize(18).margin({ bottom: 10 })
Text(this.getStatusText())
.fontSize(16).margin({ bottom: 20 })
Stack() {
Canvas(this.context)
.width(400)
.height(300)
.onReady(() => this.draw())
}
.margin({ bottom: 20 })
// 控制区域
Column() {
Text('调节离心率:')
.fontSize(16).margin({ bottom: 10 })
Row() {
Text('e:')
.width(30).fontSize(14)
Slider({
value: this.e,
min: 0.01,
max: 2.0,
step: 0.01
})
.width(200)
.onChange((value: number) => {
this.e = value
this.draw()
})
Text(this.e.toFixed(2))
.width(50).fontSize(14)
}
.margin({ bottom: 20 })
Text('圆锥曲线第二定义: 动点到焦点的距离与到准线的距离之比为离心率 e')
.fontSize(14).fontColor('#666')
.margin({ bottom: 10 })
Text('类型:')
.fontSize(14).fontColor('#666')
Text('- 0 < e < 1: 椭圆')
.fontSize(14).fontColor('#666')
Text('- e = 1: 抛物线')
.fontSize(14).fontColor('#666')
Text('- e > 1: 双曲线')
.fontSize(14).fontColor('#666')
}
}
}
getStatusText(): string {
if (this.e < 1) {
return `当前:椭圆 (0 < e < 1)`
} else if (this.e === 1) {
return `当前:抛物线 (e = 1)`
} else {
return `当前:双曲线 (e > 1)`
}
}
private draw() {
const width = 400
const height = 300
const centerX = 150 // 焦点位置
const centerY = height / 2 // 中心位置
// 清空画布
this.context.clearRect(0, 0, width, height)
this.context.fillStyle = '#F5F5F5'
this.context.fillRect(0, 0, width, height)
// 绘制坐标轴
this.drawAxes(centerX, centerY, width, height)
// 绘制准线
const directrixX = centerX - this.p / this.e
this.drawDirectrix(directrixX, height)
// 绘制焦点
this.drawFocus(centerX, centerY)
// 绘制圆锥曲线(使用极坐标方程)
this.drawConicSection(centerX, centerY, width, height)
}
private drawAxes(centerX: number, centerY: number, width: number, height: number) {
// 绘制坐标轴
this.context.strokeStyle = '#666666'
this.context.lineWidth = 2
// X轴
this.context.beginPath()
this.context.moveTo(0, centerY)
this.context.lineTo(width, centerY)
this.context.stroke()
// Y轴
this.context.beginPath()
this.context.moveTo(centerX, 0)
this.context.lineTo(centerX, height)
this.context.stroke()
// 绘制刻度
this.context.font = '10px sans-serif'
this.context.fillStyle = '#666666'
this.context.textAlign = 'center'
// X轴刻度
for (let x = centerX; x < width; x += 50) {
this.context.beginPath()
this.context.moveTo(x, centerY - 5)
this.context.lineTo(x, centerY + 5)
this.context.stroke()
if (x !== centerX) {
this.context.fillText(((x - centerX) / 50).toString(), x, centerY + 20)
}
}
// Y轴刻度
for (let y = centerY; y < height; y += 50) {
this.context.beginPath()
this.context.moveTo(centerX - 5, y)
this.context.lineTo(centerX + 5, y)
this.context.stroke()
if (y !== centerY) {
this.context.fillText(((y - centerY) / 50).toString(), centerX - 20, y + 4)
}
}
// 绘制原点
this.context.fillText('O', centerX - 10, centerY + 15)
}
private drawDirectrix(directrixX: number, height: number) {
// 绘制准线
this.context.strokeStyle = '#95A5A6'
this.context.lineWidth = 1
this.context.setLineDash([5, 5])
this.context.beginPath()
this.context.moveTo(directrixX, 0)
this.context.lineTo(directrixX, height)
this.context.stroke()
this.context.setLineDash([])
// 绘制准线标签
this.context.fillStyle = '#95A5A6'
this.context.font = '14px sans-serif'
this.context.textAlign = 'center'
this.context.fillText('准线', directrixX, 20)
}
private drawFocus(focusX: number, focusY: number) {
// 绘制焦点
this.context.fillStyle = '#E74C3C'
this.context.beginPath()
this.context.arc(focusX, focusY, 6, 0, 2 * Math.PI)
this.context.fill()
// 绘制焦点标签
this.context.fillStyle = '#E74C3C'
this.context.font = '14px sans-serif'
this.context.textAlign = 'center'
this.context.fillText('F', focusX, focusY - 15)
}
private drawConicSection(centerX: number, centerY: number, width: number, height: number) {
// 使用极坐标方程绘制圆锥曲线:r = ep / (1 - e*cosθ)
this.context.strokeStyle = '#3498DB'
this.context.lineWidth = 2
this.context.beginPath()
const step = 0.01 // 角度步长
let firstPoint = true
for (let angle = 0; angle < 2 * Math.PI; angle += step) {
const cosTheta = Math.cos(angle)
const denominator = 1 - this.e * cosTheta
// 避免分母为零或负数(对于双曲线)
if (denominator > 0.001) {
const r = (this.e * this.p) / denominator
const x = centerX + r * cosTheta
const y = centerY + r * Math.sin(angle)
// 检查点是否在画布内
if (x >= 0 && x <= width && y >= 0 && y <= height) {
if (firstPoint) {
this.context.moveTo(x, y)
firstPoint = false
} else {
this.context.lineTo(x, y)
}
}
}
}
// 对于双曲线,绘制另一支
if (this.e > 1) {
firstPoint = true
for (let angle = 0; angle < 2 * Math.PI; angle += step) {
const cosTheta = Math.cos(angle)
const denominator = 1 - this.e * cosTheta
// 绘制双曲线的另一支(分母为负)
if (denominator < -0.001) {
const r = (this.e * this.p) / denominator
const x = centerX + r * cosTheta
const y = centerY + r * Math.sin(angle)
// 检查点是否在画布内
if (x >= 0 && x <= width && y >= 0 && y <= height) {
if (firstPoint) {
this.context.moveTo(x, y)
firstPoint = false
} else {
this.context.lineTo(x, y)
}
}
}
}
}
this.context.stroke()
}
}
更多推荐



所有评论(0)