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)
  }
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐