2. 三角函数图像与性质探索

功能简介:通过调整振幅、周期、相位和垂直位移,实时展示正弦、余弦、正切函数的图像变化。支持函数叠加和复合,计算函数的定义域、值域、周期、奇偶性等性质,帮助学生理解三角函数的图像特征和参数影响。
在这里插入图片描述
ArkTS代码

@Entry
@Component
struct TrigonometricExplorer {
  @State private functionType: string = 'sin'
  @State private amplitude: number = 1
  @State private period: number = 2 * Math.PI
  @State private phase: number = 0
  @State private verticalShift: number = 0
  @State private properties: string = ''
  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('函数类型')
        .fontSize(18).fontWeight(FontWeight.Bold)
        .margin({ bottom: 10 })

      Row() {
        Button('sin')
          .width(80)
          .onClick(() => { this.functionType = 'sin'; this.updateProperties() })
        Button('cos')
          .width(80)
          .onClick(() => { this.functionType = 'cos'; this.updateProperties() })
        Button('tan')
          .width(80)
          .onClick(() => { this.functionType = 'tan'; this.updateProperties() })
      }

      Text('参数调整')
        .fontSize(18).fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 10 })

      Row() {
        Text('振幅: ')
          .width(60)
        Slider({ value: this.amplitude, min: 0.1, max: 3 })
          .width(200)
          .onChange((val: number) => { this.amplitude = val; this.updateProperties() })
        Text(this.amplitude.toFixed(1))
          .width(40)
      }
      .margin({ bottom: 10 })

      Row() {
        Text('周期: ')
          .width(60)
        Slider({ value: this.period, min: Math.PI/2, max: 4 * Math.PI, step: 0.1 })
          .width(200)
          .onChange((val: number) => { this.period = val; this.updateProperties() })
        Text((this.period / Math.PI).toFixed(1) + 'π')
          .width(60)
      }
      .margin({ bottom: 10 })

      Row() {
        Text('相位: ')
          .width(60)
        Slider({ value: this.phase, min: -Math.PI, max: Math.PI, step: 0.1 })
          .width(200)
          .onChange((val: number) => { this.phase = val; this.updateProperties() })
        Text((this.phase / Math.PI).toFixed(1) + 'π')
          .width(60)
      }
      .margin({ bottom: 10 })

      Row() {
        Text('垂直位移: ')
          .width(80)
        Slider({ value: this.verticalShift, min: -2, max: 2, step: 0.1 })
          .width(200)
          .onChange((val: number) => { this.verticalShift = val; this.updateProperties() })
        Text(this.verticalShift.toFixed(1))
          .width(40)
      }

      Canvas(this.context)
        .width(400).height(300)
        .backgroundColor('#f5f5f5')
        .margin({ top: 20 })
        .onReady(() => this.drawTrigonometric())

      Text('函数性质')
        .fontSize(18).fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 10 })

      Text(this.properties)
        .fontSize(14).fontColor('#666')

      Text('函数表达式')
        .fontSize(18).fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 10 })

      Text(this.getFunctionExpression())
        .fontSize(16).fontColor('#2196F3')
    }
    .padding(20)
  }

  private updateProperties() {
    let domain: string
    let range: string
    let parity: string
    let asymptotes: string = ''

    if (this.functionType === 'tan') {
      domain = 'x ≠ π/2 + kπ (k为整数)'
      range = '(-∞, ∞)'
      parity = '奇函数'
      asymptotes = '有垂直渐近线: x = π/2 + kπ'
    } else if (this.functionType === 'sin') {
      domain = '(-∞, ∞)'
      range = `[${-this.amplitude + this.verticalShift}, ${this.amplitude + this.verticalShift}]`
      parity = '奇函数'
    } else { // cos
      domain = '(-∞, ∞)'
      range = `[${-this.amplitude + this.verticalShift}, ${this.amplitude + this.verticalShift}]`
      parity = '偶函数'
    }

    this.properties = `定义域: ${domain}\n` +
                    `值域: ${range}\n` +
                    `周期: ${(this.period / Math.PI).toFixed(1)}π\n` +
                    `奇偶性: ${parity}\n` +
                    `${asymptotes}`
    this.drawTrigonometric()
  }

  private getFunctionExpression(): string {
    const amplitude = this.amplitude !== 1 ? this.amplitude.toFixed(1) : ''
    const phase = this.phase !== 0 ? (this.phase > 0 ? ` + ${(this.phase / Math.PI).toFixed(1)}π` : ` - ${Math.abs(this.phase / Math.PI).toFixed(1)}π`) : ''
    const verticalShift = this.verticalShift !== 0 ? (this.verticalShift > 0 ? ` + ${this.verticalShift.toFixed(1)}` : ` - ${Math.abs(this.verticalShift).toFixed(1)}`) : ''
    
    return `${amplitude}${this.functionType}(x${phase})${verticalShift}`
  }

  private drawTrigonometric() {
    const ctx = this.context
    const width = 400
    const height = 300
    const centerX = width / 2
    const centerY = height / 2
    const scaleX = 30 // x轴缩放因子
    const scaleY = 50 // y轴缩放因子
    
    // 清空画布
    ctx.clearRect(0, 0, width, height)
    
    // 绘制坐标轴
    ctx.beginPath()
    ctx.moveTo(50, centerY)
    ctx.lineTo(width - 50, centerY)
    ctx.moveTo(centerX, 50)
    ctx.lineTo(centerX, height - 50)
    ctx.strokeStyle = '#000'
    ctx.lineWidth = 1
    ctx.stroke()
    
    // 绘制网格
    ctx.strokeStyle = '#ddd'
    ctx.lineWidth = 0.5
    for (let i = 1; i < 6; i++) {
      const x = centerX + i * scaleX
      ctx.beginPath()
      ctx.moveTo(x, 50)
      ctx.lineTo(x, height - 50)
      ctx.stroke()
      
      const x2 = centerX - i * scaleX
      ctx.beginPath()
      ctx.moveTo(x2, 50)
      ctx.lineTo(x2, height - 50)
      ctx.stroke()
      
      const y = centerY + i * scaleY
      ctx.beginPath()
      ctx.moveTo(50, y)
      ctx.lineTo(width - 50, y)
      ctx.stroke()
      
      const y2 = centerY - i * scaleY
      ctx.beginPath()
      ctx.moveTo(50, y2)
      ctx.lineTo(width - 50, y2)
      ctx.stroke()
    }
    
    // 绘制函数图像
    ctx.beginPath()
    ctx.strokeStyle = '#2196F3'
    ctx.lineWidth = 2
    
    for (let x = -Math.PI * 2; x <= Math.PI * 2; x += 0.01) {
      let y: number
      try {
        if (this.functionType === 'sin') {
          y = this.amplitude * Math.sin((2 * Math.PI / this.period) * (x - this.phase)) + this.verticalShift
        } else if (this.functionType === 'cos') {
          y = this.amplitude * Math.cos((2 * Math.PI / this.period) * (x - this.phase)) + this.verticalShift
        } else { // tan
          // 跳过渐近线附近的点
          const tanArg = (2 * Math.PI / this.period) * (x - this.phase)
          if (Math.abs(Math.abs(tanArg % Math.PI) - Math.PI/2) < 0.01) {
            continue
          }
          y = this.amplitude * Math.tan(tanArg) + this.verticalShift
        }
      } catch {
        continue
      }
      
      if (Math.abs(y) > 5) continue // 限制范围
      
      const canvasX = centerX + x * scaleX
      const canvasY = centerY - y * scaleY
      
      if (x === -Math.PI * 2) {
        ctx.moveTo(canvasX, canvasY)
      } else {
        ctx.lineTo(canvasX, canvasY)
      }
    }
    ctx.stroke()
    
    // 绘制渐近线(对于正切函数)
    if (this.functionType === 'tan') {
      ctx.strokeStyle = '#FF5722'
      ctx.lineWidth = 1
      ctx.setLineDash([5, 5])
      
      for (let k = -3; k <= 3; k++) {
        const asymptoteX = centerX + (Math.PI/2 + k * Math.PI) * scaleX
        ctx.beginPath()
        ctx.moveTo(asymptoteX, 50)
        ctx.lineTo(asymptoteX, height - 50)
        ctx.stroke()
      }
      ctx.setLineDash([])
    }
  }
}

Logo

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

更多推荐