1. 函数图像变换
    • 功能:演示 y=f(x)y=f(x)y=f(x)y=f(x+a)y=f(x+a)y=f(x+a) 的平移过程,对比“左加右减”。
      演示 y=f(x) 到 y=f(x+a) 的平移过程
      支持三种函数类型:一次函数、二次函数、正弦函数
      可调节平移量a,范围从-5到5
      提供动画演示平移过程
      对比"左加右减"规律
      实时显示变换规律和函数表达式
      支持显示选项控制(原函数、变换函数、网格)
      在这里插入图片描述
// 函数图像变换
// 功能:演示 y=f(x) 到 y=f(x+a) 的平移过程,对比"左加右减"

// 函数类型
interface FunctionType {
  type: 'linear' | 'quadratic' | 'sine';
  a: number;
  b: number;
  c: number;
}

@Entry
@Component
struct FunctionTransform {
  @State canvasWidth: number = 350;
  @State canvasHeight: number = 350;
  @State showGrid: boolean = true;
  @State showAxes: boolean = true;
  @State showOriginal: boolean = true;
  @State showTransformed: boolean = true;
  @State showAnimation: boolean = false;
  @State currentShift: number = 0;
  @State targetShift: number = 0;
  @State animationProgress: number = 0;
  @State functionType: FunctionType = {
    type: 'quadratic',
    a: 1,
    b: 0,
    c: 0
  };
  
  build() {
    Column({ space: 15 }) {
      Text('函数图像变换')
        .fontSize(26)
        .fontWeight(FontWeight.Bold)
        .textAlign(TextAlign.Center)

      Column() {
        Text('功能介绍')
          .fontSize(18)
          .fontWeight(FontWeight.Medium)
        Text('演示 y=f(x) 到 y=f(x+a) 的平移过程,对比"左加右减"规律,直观理解函数图像的水平平移变换')
          .fontSize(14)
          .fontColor('#666666')
          .textAlign(TextAlign.Center)
      }
      .width('100%')
      .backgroundColor('#E3F2FD')
      .borderRadius(10)
      .padding(15)

      Column({ space: 10 }) {
        Text('函数图像')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .textAlign(TextAlign.Center)
        
        Canvas(this.canvasContext)
          .width(this.canvasWidth)
          .height(this.canvasHeight)
          .backgroundColor('#FFFFFF')
          .border({ width: 2, color: '#333' })
          .borderRadius(10)
          .onReady(() => {
            this.drawGraph()
          })
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#FAFAFA')
      .borderRadius(10)

      Column({ space: 10 }) {
        Text('函数选择')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .textAlign(TextAlign.Center)
        
        Row({ space: 10 }) {
          Button('一次函数')
            .width('31%')
            .height(40)
            .fontSize(14)
            .backgroundColor(this.functionType.type === 'linear' ? '#2196F3' : '#E0E0E0')
            .onClick(() => {
              this.functionType = {
                type: 'linear',
                a: 1,
                b: 0,
                c: 0
              }
              this.drawGraph()
            })
          
          Button('二次函数')
            .width('31%')
            .height(40)
            .fontSize(14)
            .backgroundColor(this.functionType.type === 'quadratic' ? '#2196F3' : '#E0E0E0')
            .onClick(() => {
              this.functionType = {
                type: 'quadratic',
                a: 1,
                b: 0,
                c: 0
              }
              this.drawGraph()
            })
          
          Button('正弦函数')
            .width('31%')
            .height(40)
            .fontSize(14)
            .backgroundColor(this.functionType.type === 'sine' ? '#2196F3' : '#E0E0E0')
            .onClick(() => {
              this.functionType = {
                type: 'sine',
                a: 1,
                b: 1,
                c: 0
              }
              this.drawGraph()
            })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
      }
      .width('90%')
      .padding(10)
      .backgroundColor('#FAFAFA')
      .borderRadius(10)

      Column({ space: 10 }) {
        Text('平移控制')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .textAlign(TextAlign.Center)
        
        Column({ space: 8 }) {
          Text(`平移量 a: ${this.targetShift.toFixed(2)}`)
            .fontSize(16)
            .fontColor('#666666')
            .fontWeight(FontWeight.Medium)
          
          Slider({
            value: this.targetShift,
            min: -5,
            max: 5,
            step: 0.1
          })
            .width('100%')
            .blockColor('#FF9800')
            .trackColor('#E0E0E0')
            .selectedColor('#FF9800')
            .onChange((value: number) => {
              this.targetShift = value
              if (!this.showAnimation) {
                this.currentShift = value
                this.drawGraph()
              }
            })
        }
        .width('100%')
        .padding(10)
        .backgroundColor('#F5F5F5')
        .borderRadius(8)

        Row({ space: 10 }) {
          Button('开始动画')
            .width('48%')
            .height(45)
            .fontSize(16)
            .backgroundColor('#4CAF50')
            .onClick(() => {
              this.startAnimation()
            })
          
          Button('重置')
            .width('48%')
            .height(45)
            .fontSize(16)
            .backgroundColor('#FF9800')
            .onClick(() => {
              this.reset()
            })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
      }
      .width('90%')
      .padding(10)
      .backgroundColor('#FAFAFA')
      .borderRadius(10)

      Column({ space: 10 }) {
        Text('变换规律')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .textAlign(TextAlign.Center)
        
        Column({ space: 8 }) {
          Text(`原函数: y = ${this.getOriginalExpression()}`)
            .fontSize(16)
            .fontColor('#2196F3')
            .textAlign(TextAlign.Center)
          
          Text(`变换函数: y = ${this.getTransformedExpression()}`)
            .fontSize(16)
            .fontColor('#4CAF50')
            .textAlign(TextAlign.Center)
          
          if (this.targetShift > 0) {
            Text(`平移规律: 向左平移 ${this.targetShift.toFixed(2)} 个单位`)
              .fontSize(16)
              .fontColor('#FF9800')
              .fontWeight(FontWeight.Bold)
              .textAlign(TextAlign.Center)
          } else if (this.targetShift < 0) {
            Text(`平移规律: 向右平移 ${Math.abs(this.targetShift).toFixed(2)} 个单位`)
              .fontSize(16)
              .fontColor('#FF9800')
              .fontWeight(FontWeight.Bold)
              .textAlign(TextAlign.Center)
          } else {
            Text(`平移规律: 无平移`)
              .fontSize(16)
              .fontColor('#999999')
              .fontWeight(FontWeight.Bold)
              .textAlign(TextAlign.Center)
          }
          
          Text(`"左加右减": a > 0 时向左,a < 0 时向右`)
            .fontSize(14)
            .fontColor('#666666')
            .textAlign(TextAlign.Center)
        }
        .width('100%')
        .padding(15)
        .backgroundColor('#E8F5E9')
        .borderRadius(10)
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#FAFAFA')
      .borderRadius(10)

      Column({ space: 8 }) {
        Text('显示选项')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        
        Row({ space: 10 }) {
          Row({ space: 5 }) {
            Text('显示原函数')
              .fontSize(14)
            Text(this.showOriginal ? '✓' : '✗')
              .fontSize(14)
              .fontColor(this.showOriginal ? '#4CAF50' : '#F44336')
          }
          .onClick(() => {
            this.showOriginal = !this.showOriginal
            this.drawGraph()
          })
          
          Row({ space: 5 }) {
            Text('显示变换函数')
              .fontSize(14)
            Text(this.showTransformed ? '✓' : '✗')
              .fontSize(14)
              .fontColor(this.showTransformed ? '#4CAF50' : '#F44336')
          }
          .onClick(() => {
            this.showTransformed = !this.showTransformed
            this.drawGraph()
          })
          
          Row({ space: 5 }) {
            Text('显示网格')
              .fontSize(14)
            Text(this.showGrid ? '✓' : '✗')
              .fontSize(14)
              .fontColor(this.showGrid ? '#4CAF50' : '#F44336')
          }
          .onClick(() => {
            this.showGrid = !this.showGrid
            this.drawGraph()
          })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceAround)
      }
      .width('90%')
      .padding(10)
      .backgroundColor('#F5F5F5')
      .borderRadius(8)

      Column({ space: 8 }) {
        Text('使用说明')
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
        
        Text('• 选择函数类型(一次、二次、正弦)')
          .fontSize(14)
          .fontColor('#666666')
        
        Text('• 调节平移量a,观察图像变化')
          .fontSize(14)
          .fontColor('#666666')
        
        Text('• 点击"开始动画"演示平移过程')
          .fontSize(14)
          .fontColor('#666666')
        
        Text('• 理解"左加右减"的规律')
          .fontSize(14)
          .fontColor('#666666')
        
        Text('• a > 0 时向左平移,a < 0 时向右平移')
          .fontSize(14)
          .fontColor('#666666')
      }
      .width('95%')
      .padding(12)
      .backgroundColor('#FFF3E0')
      .borderRadius(10)
    }
    .width('100%')
    .height('100%')
    .padding(10)
    .justifyContent(FlexAlign.Start)
  }

  private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D();

  private startAnimation() {
    this.showAnimation = true
    this.animationProgress = 0
    const startValue = this.currentShift
    const endValue = this.targetShift
    const totalSteps = 50
    
    let step = 0
    const animate = () => {
      if (step <= totalSteps) {
        this.animationProgress = step / totalSteps
        this.currentShift = startValue + (endValue - startValue) * this.animationProgress
        this.drawGraph()
        step++
        setTimeout(animate, 30)
      } else {
        this.showAnimation = false
      }
    }
    animate()
  }

  private reset() {
    this.targetShift = 0
    this.currentShift = 0
    this.animationProgress = 0
    this.showAnimation = false
    this.drawGraph()
  }

  private getOriginalExpression(): string {
    switch (this.functionType.type) {
      case 'linear':
        return `${this.functionType.a.toFixed(2)}x + ${this.functionType.b.toFixed(2)}`
      case 'quadratic':
        return `${this.functionType.a.toFixed(2)}x² + ${this.functionType.b.toFixed(2)}x + ${this.functionType.c.toFixed(2)}`
      case 'sine':
        return `${this.functionType.a.toFixed(2)}sin(${this.functionType.b.toFixed(2)}x)`
    }
  }

  private getTransformedExpression(): string {
    const shiftValue = this.targetShift
    const shiftStr = this.targetShift.toFixed(2)
    const sign = shiftValue >= 0 ? '+' : ''
    switch (this.functionType.type) {
      case 'linear':
        return `${this.functionType.a.toFixed(2)}(x ${sign}${shiftStr}) + ${this.functionType.b.toFixed(2)}`
      case 'quadratic':
        return `${this.functionType.a.toFixed(2)}(x ${sign}${shiftStr})² + ${this.functionType.b.toFixed(2)}(x ${sign}${shiftStr}) + ${this.functionType.c.toFixed(2)}`
      case 'sine':
        return `${this.functionType.a.toFixed(2)}sin(${this.functionType.b.toFixed(2)}(x ${sign}${shiftStr}))`
    }
  }

  private drawGraph() {
    const ctx = this.canvasContext
    ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
    
    if (this.showGrid) {
      this.drawGridLines(ctx)
    }
    
    if (this.showAxes) {
      this.drawAxes(ctx)
    }
    
    if (this.showOriginal) {
      this.drawFunction(ctx, 0, '#2196F3', '原函数')
    }
    
    if (this.showTransformed) {
      this.drawFunction(ctx, this.currentShift, '#4CAF50', '变换函数')
    }
  }

  private drawGridLines(ctx: CanvasRenderingContext2D) {
    ctx.strokeStyle = '#E0E0E0'
    ctx.lineWidth = 1
    
    const centerX = this.canvasWidth / 2
    const centerY = this.canvasHeight / 2
    const scale = 30
    
    for (let i = -5; i <= 5; i++) {
      ctx.beginPath()
      ctx.moveTo(centerX + i * scale, 0)
      ctx.lineTo(centerX + i * scale, this.canvasHeight)
      ctx.stroke()
    }
    
    for (let i = -5; i <= 5; i++) {
      ctx.beginPath()
      ctx.moveTo(0, centerY + i * scale)
      ctx.lineTo(this.canvasWidth, centerY + i * scale)
      ctx.stroke()
    }
  }

  private drawAxes(ctx: CanvasRenderingContext2D) {
    const centerX = this.canvasWidth / 2
    const centerY = this.canvasHeight / 2
    
    ctx.strokeStyle = '#333'
    ctx.lineWidth = 2
    
    ctx.beginPath()
    ctx.moveTo(centerX, 0)
    ctx.lineTo(centerX, this.canvasHeight)
    ctx.stroke()
    
    ctx.beginPath()
    ctx.moveTo(0, centerY)
    ctx.lineTo(this.canvasWidth, centerY)
    ctx.stroke()
    
    ctx.fillStyle = '#333'
    ctx.font = '12px sans-serif'
    ctx.fillText('x', this.canvasWidth - 15, centerY + 15)
    ctx.fillText('y', centerX + 5, 15)
    ctx.fillText('O', centerX + 5, centerY + 15)
    
    const scale = 30
    for (let i = -5; i <= 5; i++) {
      if (i !== 0) {
        ctx.fillText(i.toString(), centerX + i * scale - 3, centerY + 20)
        ctx.fillText((-i).toString(), centerX - 15, centerY + i * scale + 3)
      }
    }
  }

  private drawFunction(ctx: CanvasRenderingContext2D, shift: number, color: string, label: string) {
    const centerX = this.canvasWidth / 2
    const centerY = this.canvasHeight / 2
    const scale = 30
    
    ctx.strokeStyle = color
    ctx.lineWidth = 3
    ctx.beginPath()
    
    for (let x = -5; x <= 5; x += 0.05) {
      let y: number
      
      switch (this.functionType.type) {
        case 'linear':
          y = this.functionType.a * (x + shift) + this.functionType.b
          break
        case 'quadratic':
          y = this.functionType.a * (x + shift) * (x + shift) + this.functionType.b * (x + shift) + this.functionType.c
          break
        case 'sine':
          y = this.functionType.a * Math.sin(this.functionType.b * (x + shift))
          break
      }
      
      const pixelX = centerX + x * scale
      const pixelY = centerY - y * scale
      
      if (x === -5) {
        ctx.moveTo(pixelX, pixelY)
      } else {
        ctx.lineTo(pixelX, pixelY)
      }
    }
    ctx.stroke()
    
    ctx.fillStyle = color
    ctx.font = '14px sans-serif'
    ctx.fillText(label, 10, label === '原函数' ? 30 : 50)
  }
}
Logo

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

更多推荐