鸿蒙 ArkTS 实战|杠杆平衡计算器:Canvas 画力学示意 + 三类杠杆判定
·



一、设计思路
杠杆是力学第一课,核心只有一条公式: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₂:天平、定滑轮(不省力不费力) |
| 易错 | 力臂是"支点到力的作用线的距离",不是支点到作用点的距离 |
| 口诀 | 动力臂长省力费距离,动力臂短费力省距离 |
更多推荐




所有评论(0)