1. 变量与函数关系
    • 功能:模拟弹簧伸长量与重物质量的关系,记录数据点,绘制图像,引入函数概念。
      应用功能:
      模拟弹簧伸长量与重物质量的关系
      可调节弹簧劲度系数
      可调节重物质量,实时计算弹簧伸长量
      记录数据点功能,保存实验数据
      绘制函数图像,直观展示变量关系
      显示/隐藏网格和函数线
      显示数据记录表格
      展示函数概念和数学表达式
      清空数据和重置功能
      在这里插入图片描述
// 变量与函数关系
// 功能:模拟弹簧伸长量与重物质量的关系,记录数据点,绘制图像,引入函数概念
// 数据点
interface DataPoint {
  mass: number;
  extension: number;
}

@Entry
@Component
struct VariableFunctionRelation {
  @State springConstant: number = 10;
  @State currentMass: number = 50;
  @State currentExtension: number = 0;
  @State dataPoints: Array<DataPoint> = [];
  @State showFunction: boolean = true;
  @State showGrid: boolean = true;
  @State canvasWidth: number = 350;
  @State canvasHeight: number = 250;
  
  build() {
    Column({ space: 15 }) {
      Text('变量与函数关系')
        .fontSize(26)
        .fontWeight(FontWeight.Bold)
        .textAlign(TextAlign.Center)

      Column() {
        Text('功能介绍')
          .fontSize(18)
          .fontWeight(FontWeight.Medium)
        Text('模拟弹簧伸长量与重物质量的关系,记录数据点,绘制图像,引入函数概念')
          .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)
        
        Column({ space: 8 }) {
          Text(`弹簧劲度系数: ${this.springConstant} N/m`)
            .fontSize(14)
            .fontColor('#666666')
          
          Slider({
            value: this.springConstant,
            min: 5,
            max: 20,
            step: 1,
            style: SliderStyle.OutSet
          })
            .width('100%')
            .blockColor('#2196F3')
            .trackColor('#E0E0E0')
            .selectedColor('#2196F3')
            .onChange((value: number) => {
              this.springConstant = value
              this.calculateExtension()
            })
        }
        .width('100%')
        .padding(10)
        .backgroundColor('#F5F5F5')
        .borderRadius(8)

        Column({ space: 8 }) {
          Text(`当前质量: ${this.currentMass} g`)
            .fontSize(14)
            .fontColor('#666666')
          
          Slider({
            value: this.currentMass,
            min: 0,
            max: 200,
            step: 10,
            style: SliderStyle.OutSet
          })
            .width('100%')
            .blockColor('#FF5722')
            .trackColor('#E0E0E0')
            .selectedColor('#FF5722')
            .onChange((value: number) => {
              this.currentMass = value
              this.calculateExtension()
            })
          
          Text(`弹簧伸长量: ${this.currentExtension.toFixed(2)} cm`)
            .fontSize(14)
            .fontColor('#666666')
        }
        .width('100%')
        .padding(10)
        .backgroundColor('#F5F5F5')
        .borderRadius(8)

        Button('记录数据点')
          .width('100%')
          .height(45)
          .fontSize(16)
          .backgroundColor('#4CAF50')
          .onClick(() => {
            this.recordDataPoint()
          })
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#FAFAFA')
      .borderRadius(10)

      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: 8 }) {
        Text('显示选项')
          .fontSize(14)
          .fontWeight(FontWeight.Medium)
        
        Row({ space: 10 }) {
          Row({ space: 5 }) {
            Text('显示网格')
              .fontSize(14)
            Text(this.showGrid ? '✓' : '✗')
              .fontSize(14)
              .fontColor(this.showGrid ? '#4CAF50' : '#F44336')
          }
          .onClick(() => {
            this.showGrid = !this.showGrid
            this.drawGraph()
          })
          
          Row({ space: 5 }) {
            Text('显示函数')
              .fontSize(14)
            Text(this.showFunction ? '✓' : '✗')
              .fontSize(14)
              .fontColor(this.showFunction ? '#4CAF50' : '#F44336')
          }
          .onClick(() => {
            this.showFunction = !this.showFunction
            this.drawGraph()
          })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceAround)
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#F5F5F5')
      .borderRadius(8)

      Column({ space: 10 }) {
        Text('数据记录')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .textAlign(TextAlign.Center)
        
        if (this.dataPoints.length > 0) {
          Column({ space: 5 }) {
            Row({ space: 10 }) {
              Text('序号')
                .fontSize(14)
                .fontWeight(FontWeight.Bold)
                .width('20%')
              Text('质量(g)')
                .fontSize(14)
                .fontWeight(FontWeight.Bold)
                .width('40%')
              Text('伸长量(cm)')
                .fontSize(14)
                .fontWeight(FontWeight.Bold)
                .width('40%')
            }
            .width('100%')
            .padding(8)
            .backgroundColor('#E3F2FD')
            .borderRadius(5)
            
            ForEach(this.dataPoints, (point: DataPoint, index: number) => {
              Row({ space: 10 }) {
                Text(`${index + 1}`)
                  .fontSize(14)
                  .width('20%')
                Text(`${point.mass}`)
                  .fontSize(14)
                  .width('40%')
                Text(`${point.extension.toFixed(2)}`)
                  .fontSize(14)
                  .width('40%')
              }
              .width('100%')
              .padding(8)
              .backgroundColor('#FFFFFF')
              .borderRadius(5)
            })
          }
          .width('100%')
          .padding(10)
          .backgroundColor('#FAFAFA')
          .borderRadius(8)
        } else {
          Text('暂无数据,请添加数据点')
            .fontSize(14)
            .fontColor('#999999')
            .textAlign(TextAlign.Center)
            .width('100%')
            .padding(20)
        }
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#FAFAFA')
      .borderRadius(10)

      Column({ space: 8 }) {
        Text('函数概念')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .textAlign(TextAlign.Center)
        
        Text(`函数关系: y = ${(1 / this.springConstant).toFixed(3)}x`)
          .fontSize(16)
          .fontColor('#2196F3')
          .textAlign(TextAlign.Center)
        
        Text('其中:')
          .fontSize(14)
          .fontColor('#666666')
        
        Text('• x: 重物质量 (g)')
          .fontSize(14)
          .fontColor('#666666')
        
        Text(`• y: 弹簧伸长量 (cm)`)
          .fontSize(14)
          .fontColor('#666666')
        
        Text(`• k = ${(1 / this.springConstant).toFixed(3)}: 弹簧常数`)
          .fontSize(14)
          .fontColor('#666666')
      }
      .width('100%')
      .padding(15)
      .backgroundColor('#FFF3E0')
      .borderRadius(10)

      Row({ space: 10 }) {
        Button('清空数据')
          .width('48%')
          .height(50)
          .fontSize(16)
          .backgroundColor('#F44336')
          .onClick(() => {
            this.clearData()
          })
        
        Button('重置')
          .width('48%')
          .height(50)
          .fontSize(16)
          .backgroundColor('#FF9800')
          .onClick(() => {
            this.reset()
          })
      }
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .padding(10)
    .justifyContent(FlexAlign.Start)
  }

  private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D();

  private calculateExtension() {
    this.currentExtension = (this.currentMass / this.springConstant)
    this.drawGraph()
  }

  private recordDataPoint() {
    if (this.currentMass > 0) {
      this.dataPoints.push({
        mass: this.currentMass,
        extension: this.currentExtension
      })
      this.drawGraph()
    }
  }

  private drawGraph() {
    const ctx = this.canvasContext
    ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
    
    if (this.showGrid) {
      this.drawGridLines(ctx)
    }
    
    this.drawAxes(ctx)
    this.drawDataPoints(ctx)
    
    if (this.showFunction) {
      this.drawFunctionLine(ctx)
    }
  }

  private drawGridLines(ctx: CanvasRenderingContext2D) {
    ctx.strokeStyle = '#E0E0E0'
    ctx.lineWidth = 1
    
    const gridSize = 25
    for (let i = 0; i <= this.canvasWidth; i += gridSize) {
      ctx.beginPath()
      ctx.moveTo(i, 0)
      ctx.lineTo(i, this.canvasHeight)
      ctx.stroke()
    }
    
    for (let i = 0; i <= this.canvasHeight; i += gridSize) {
      ctx.beginPath()
      ctx.moveTo(0, i)
      ctx.lineTo(this.canvasWidth, i)
      ctx.stroke()
    }
  }

  private drawAxes(ctx: CanvasRenderingContext2D) {
    ctx.strokeStyle = '#333'
    ctx.lineWidth = 2
    
    const originX = 40
    const originY = this.canvasHeight - 30
    
    ctx.beginPath()
    ctx.moveTo(originX, 0)
    ctx.lineTo(originX, this.canvasHeight)
    ctx.stroke()
    
    ctx.beginPath()
    ctx.moveTo(0, originY)
    ctx.lineTo(this.canvasWidth, originY)
    ctx.stroke()
    
    ctx.fillStyle = '#333'
    ctx.font = '12px sans-serif'
    ctx.fillText('质量(g)', this.canvasWidth - 60, originY + 20)
    ctx.fillText('伸长量(cm)', 5, 20)
  }

  private drawDataPoints(ctx: CanvasRenderingContext2D) {
    const originX = 40
    const originY = this.canvasHeight - 30
    const scaleX = (this.canvasWidth - 60) / 200
    const scaleY = (this.canvasHeight - 50) / 20
    
    ctx.fillStyle = '#FF5722'
    ctx.strokeStyle = '#FF5722'
    
    for (const point of this.dataPoints) {
      const x = originX + point.mass * scaleX
      const y = originY - point.extension * scaleY
      
      ctx.beginPath()
      ctx.arc(x, y, 5, 0, 2 * Math.PI)
      ctx.fill()
      ctx.stroke()
    }
  }

  private drawFunctionLine(ctx: CanvasRenderingContext2D) {
    const originX = 40
    const originY = this.canvasHeight - 30
    const scaleX = (this.canvasWidth - 60) / 200
    const scaleY = (this.canvasHeight - 50) / 20
    
    ctx.strokeStyle = '#2196F3'
    ctx.lineWidth = 2
    ctx.beginPath()
    
    for (let mass = 0; mass <= 200; mass += 5) {
      const extension = mass / this.springConstant
      const x = originX + mass * scaleX
      const y = originY - extension * scaleY
      
      if (mass === 0) {
        ctx.moveTo(x, y)
      } else {
        ctx.lineTo(x, y)
      }
    }
    
    ctx.stroke()
  }

  private clearData() {
    this.dataPoints = []
    this.drawGraph()
  }

  private reset() {
    this.springConstant = 10
    this.currentMass = 50
    this.currentExtension = 0
    this.dataPoints = []
    this.showFunction = true
    this.showGrid = true
    this.calculateExtension()
  }
}
Logo

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

更多推荐