在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、设计思路

杠杆是力学第一课,核心只有一条公式:F₁ × L₁ = F₂ × L₂。它的技术点在于:①公式反推计算(由平衡条件求所需动力);②按力臂长短自动判定"省力/费力/等臂"三类杠杆;③用 Canvas 2D 动态画出支点、横杆、力臂与双向力箭头,让抽象力学可视化。页面里输入力臂和阻力,点击"计算"即得结果,示意图同步刷新。

二、数据模型与核心计算(页面内逻辑)

import { router } from '@kit.ArkUI';

// 计算结果的数据结构:力 + 杠杆类型 + 实例 + 说明
interface LeverResult {
  force: string;    // 计算出的动力 F1
  type: string;     // 省力杠杆 / 费力杠杆 / 等臂杠杆
  example: string;  // 对应生活实例
  tip: string;      // 判定说明
}

@Entry
@Component
struct Lever {
  // 三个输入参数:动力臂、阻力臂、阻力 —— @State 驱动计算与重绘
  @State effortArm: string = '20';   // 动力臂 L1(cm)
  @State loadArm: string = '10';     // 阻力臂 L2(cm)
  @State loadForce: string = '100';  // 阻力 F2(N)
  @State result: LeverResult = {
    force: '50.0 N', type: '省力杠杆',
    example: '撬棒、开瓶器、羊角锤、铡刀',
    tip: '动力臂 20cm > 阻力臂 10cm:省力但费距离'
  };

  // Canvas 2D 上下文(RenderingContextSettings 开启抗锯齿)
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);

  // 核心计算:由平衡条件反推动力 F1 = F2 × L2 / L1
  private calculate(): void {
    const l1 = Number(this.effortArm);
    const l2 = Number(this.loadArm);
    const f2 = Number(this.loadForce);
    if (l1 <= 0 || l2 <= 0 || f2 <= 0) {
      this.result = { force: '输入无效', type: '', example: '', tip: '力臂与力必须为正数' };
      return;
    }
    const f1 = f2 * l2 / l1;
    let type = '';
    let example = '';
    let tip = '';
    if (l1 > l2) {
      type = '省力杠杆';
      example = '撬棒、开瓶器、羊角锤、铡刀';
      tip = `动力臂(L1=${l1}cm) > 阻力臂(L2=${l2}cm):省力但费距离`;
    } else if (l1 < l2) {
      type = '费力杠杆';
      example = '钓鱼竿、镊子、筷子、船桨';
      tip = `动力臂(L1=${l1}cm) < 阻力臂(L2=${l2}cm):费力但省距离`;
    } else {
      type = '等臂杠杆';
      example = '天平、定滑轮(可视为等臂杠杆)';
      tip = `动力臂 = 阻力臂 = ${l1}cm:不省力也不费力`;
    }
    this.result = { force: f1.toFixed(1) + ' N', type, example, tip };
  }
}

三、Canvas 绘制杠杆示意图

  // Canvas 绘制杠杆:支点 + 横杆 + 两端力箭头
  private draw(): void {
    const ctx = this.ctx;
    const w = ctx.width;   // Canvas 实际像素宽(自适应屏幕)
    const h = ctx.height;
    ctx.clearRect(0, 0, w, h);
    const cy = h * 0.62;

    // 横杆(粗线)
    ctx.strokeStyle = '#4C7DFF';
    ctx.lineWidth = 10;
    ctx.beginPath();
    ctx.moveTo(w * 0.14, cy);
    ctx.lineTo(w * 0.86, cy);
    ctx.stroke();

    // 支点三角形(位于中间)
    ctx.fillStyle = '#6B7280';
    ctx.beginPath();
    ctx.moveTo(w * 0.5, cy - 6);
    ctx.lineTo(w * 0.5 - 16, cy + 26);
    ctx.lineTo(w * 0.5 + 16, cy + 26);
    ctx.closePath();
    ctx.fill();
    ctx.fillRect(w * 0.5 - 30, cy + 24, 60, 6);   // 底座

    // 动力箭头(左端向上)与阻力箭头(右端向下)
    this.arrow(w * 0.14, cy + 26, w * 0.14, cy - 60, '#10B981');
    this.label('F₁ 动力', w * 0.14, cy - 72, '#10B981');
    this.arrow(w * 0.86, cy - 26, w * 0.86, cy + 58, '#EF4444');
    this.label('F₂ 阻力', w * 0.86, cy + 74, '#EF4444');
  }

  // 通用箭头绘制:线段 + 箭头头部(atan2 计算方向角)
  private arrow(fromX: number, fromY: number, toX: number, toY: number, color: string): void {
    const ctx = this.ctx;
    ctx.strokeStyle = color;
    ctx.lineWidth = 3;
    ctx.beginPath();
    ctx.moveTo(fromX, fromY);
    ctx.lineTo(toX, toY);
    ctx.stroke();
    const angle = Math.atan2(toY - fromY, toX - fromX);
    const head = 9;
    ctx.beginPath();
    ctx.moveTo(toX, toY);
    ctx.lineTo(toX - head * Math.cos(angle - Math.PI / 6),
                toY - head * Math.sin(angle - Math.PI / 6));
    ctx.moveTo(toX, toY);
    ctx.lineTo(toX - head * Math.cos(angle + Math.PI / 6),
                toY - head * Math.sin(angle + Math.PI / 6));
    ctx.stroke();
  }

  private label(text: string, x: number, y: number, color: string): void {
    const ctx = this.ctx;
    ctx.fillStyle = color;
    ctx.font = '13px sans-serif';
    ctx.textAlign = 'center';
    ctx.fillText(text, x, y);
  }

四、UI 组装(输入区 + 计算按钮 + 结果卡)

  build() {
    Column() {
      // 顶部标题栏(返回 + 标题)
      Row() {
        Text('‹').fontSize(34).fontColor(Color.White)
          .onClick(() => router.back())
        Text('杠杆 · 平衡条件').fontSize(19).fontWeight(FontWeight.Bold)
          .fontColor(Color.White).margin({ left: 8 })
        Blank()
      }
      .width('100%').height(56).padding({ left: 10, right: 16 })
      .backgroundColor('#10B981')

      Scroll() {
        Column({ space: 14 }) {
          // 公式卡
          Column({ space: 6 }) {
            Text('平衡条件').fontSize(12).fontColor('#9CA3AF').width('100%')
            Text('F₁ × L₁ = F₂ × L₂')
              .fontSize(22).fontWeight(FontWeight.Bold)
              .fontColor('#10B981').width('100%').textAlign(TextAlign.Center)
          }
          .width('100%').padding(16).borderRadius(16)
          .backgroundColor(Color.White).border({ width: 1, color: '#E5EAF2' })

          // 示意图:Canvas 挂到页面,onReady 时首绘
          Canvas(this.ctx)
            .width('100%').height(200)
            .onReady(() => { this.draw(); })

          // 三个参数输入行
          Column({ space: 12 }) {
            Row({ space: 10 }) {
              Text('动力臂 L₁(cm)').fontSize(14).fontColor('#111827').layoutWeight(1)
              TextInput({ text: this.effortArm })
                .width(110).height(36).type(InputType.Number)
                .onChange((v: string) => { this.effortArm = v; })
            }.width('100%')
            // 阻力臂、阻力输入行同理(略)

            Button('计算所需动力 F₁').width('100%').height(42)
              .backgroundColor('#10B981')
              .onClick(() => {
                this.calculate();   // 更新结果
                this.draw();        // 重绘示意图(力臂标注同步)
              })
          }
          .width('100%').padding(16).borderRadius(16)
          .backgroundColor(Color.White).border({ width: 1, color: '#E5EAF2' })

          // 结果卡:@State result 变化自动刷新
          Column({ space: 8 }) {
            Text('计算结果').fontSize(15).fontWeight(FontWeight.Bold)
              .fontColor('#111827').width('100%')
            Row({ space: 12 }) {
              Text(this.result.force)
                .fontSize(26).fontWeight(FontWeight.Bold).fontColor('#10B981')
              Text(this.result.type)
                .fontSize(13).fontColor(Color.White)
                .backgroundColor('#10B981').padding({ left: 10, right: 10, top: 3, bottom: 3 })
                .borderRadius(10)
            }.width('100%').margin({ top: 4 })
            Text('实例:' + this.result.example)
              .fontSize(13).fontColor('#4B5563').width('100%').lineHeight(20)
            Text('💡 ' + this.result.tip)
              .fontSize(13).fontColor('#B45309').width('100%').lineHeight(20)
          }
          .width('100%').padding(16).borderRadius(16)
          .backgroundColor('#E6FFF9').border({ width: 1, color: '#A7F3D0' })
        }
        .padding(14)
      }
      .layoutWeight(1).scrollBar(BarState.Off)
    }
    .height('100%').width('100%').backgroundColor('#F4F6FA')
  }

五、代码要点

  • 公式反推f1 = f2 * l2 / l1 一行实现平衡条件求解;Number() 把输入字符串转数值,toFixed(1) 保留一位小数。
  • 条件判定l1 > l2 → 省力;l1 < l2 → 费力;相等 → 等臂,三类杠杆一次判定。
  • Canvas 自适应:坐标全部用 w * 0.xx 比例计算,任何屏宽下不变形;atan2 算箭头方向角。
  • 声明式刷新@State result 变化后结果卡自动重绘,draw() 手动同步示意图。

六、杠杆核心知识点

要点内容
定义一根在力的作用下能绕固定点转动的硬棒
五要素支点 O、动力 F₁、阻力 F₂、动力臂 L₁、阻力臂 L₂
平衡条件F₁ × L₁ = F₂ × L₂(杠杆平衡 = 匀速转动或静止)
省力杠杆L₁ > L₂:撬棒、开瓶器、羊角锤、铡刀、指甲剪(费距离)
费力杠杆L₁ < L₂:钓鱼竿、镊子、筷子、船桨、理发剪(省距离)
等臂杠杆L₁ = L₂:天平、定滑轮(不省力不费力)
易错力臂是"支点到力的作用线的距离",不是支点到作用点的距离
口诀动力臂长省力费距离,动力臂短费力省距离
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐