Harmonyos应用实例234:整数加减法数轴演示
·
模块一:数与代数 (1-10)
- 整数加减法数轴演示
- 功能:在数轴上动态移动小人来演示加减法运算,支持负数运算,帮助理解“向右加、向左减”的几何意义。
这是一个使用 HarmonyOS (ArkTS) 编写的教学应用项目。该项目通过可视化交互方式,帮助学生理解有理数加减法在数轴上的几何意义。
项目名称:数轴上的加减法探险
功能介绍:
本应用提供了一个交互式数轴界面,屏幕上有一个可爱的“小人”代表当前数值位置。用户可以通过点击数字按钮选择移动步数,通过选择“加”或“减”决定运算方式。点击“移动”后,小人会以流畅的动画效果向右(加法)或向左(减法)移动,并实时显示运算算式和结果。特别针对负数运算,如“0 - 3”,小人会直观地向左跨越零点,帮助学生形象理解“减去一个数即向数轴负方向移动”的抽象概念。
布局结构:
使用 Stack 容器将 Canvas 绘制的数轴背景和动态移动的小人(Text 组件)叠加在一起。
currentPos 是核心状态变量,表示小人在数轴上的逻辑位置(如 -3, 0, 5)。
动画机制:
小人的位置通过 .translate({ x: this.currentPos * this.unitWidth }) 进行偏移。
. animation() 属性修饰在组件上,当 currentPos 状态改变时,系统会自动计算偏移量并生成平滑的过渡动画,模拟行走效果。
教学逻辑:
正向移动:点击“加法”,currentPos 增加,小人向右移动。
负向移动:点击“减法”,currentPos 减少,小人向左移动。
边界处理:设置了 -10 到 10 的边界,防止小人移出屏幕,并给予 Toast 提示。
@Entry
@Component
struct NumberLineAdventure {
// 状态变量:当前位置 (逻辑坐标,如 -5, 0, 3)
@State currentPos: number = 0
// 状态变量:移动步数
@State moveSteps: number = 1
// 状态变量:运算符号 (true为加法,false为减法)
@State isAddition: boolean = true
// 状态变量:动画执行状态
@State isMoving: boolean = false
// 动画相关常量
private unitWidth: number = 30 // 数轴单位像素宽度
private rangeMin: number = -10 // 数轴最小值
private rangeMax: number = 10 // 数轴最大值
// Canvas 上下文
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
build() {
Scroll() {
Column({ space: 15 }) {
// 标题区
Text('数轴加减法演示')
.fontSize(28)
.fontWeight(FontWeight.Bold)
Text('观察小人如何移动:向右加,向左减')
.fontSize(14)
.fontColor('#666')
// ================= 核心可视化区域 =================
Stack() {
// 1. 背景:数轴刻度 (使用Canvas绘制)
Canvas(this.context)
.width('100%')
.height(150)
.backgroundColor('#F0F4F8')
.onReady(() => {
this.drawNumberLine()
})
// 2. 前景:移动的小人 (使用Stack定位)
Row() {
// 小人组件
Column() {
Text('🏃')
.fontSize(30)
Text(`${this.currentPos}`)
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.backgroundColor('#FF5722')
.borderRadius(10)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
}
// 关键动画逻辑:根据 currentPos 计算偏移量
.offset({ x: this.currentPos * this.unitWidth+20, y: 0 })
.animation({ duration: 500, curve: Curve.EaseOut }) // 启用平滑动画
}
.width('100%')
.height(150)
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Top)
}
.width('100%')
.height(150)
.backgroundColor('#F0F4F8')
.borderRadius(15)
// ================= 运算结果显示 =================
Column({ space: 10 }) {
Text('当前位置:')
.fontSize(14)
.fontColor('#666')
Text(`${this.currentPos}`)
.fontSize(36)
.fontWeight(FontWeight.Bold)
.fontColor('#2196F3')
Text('即将执行:')
.fontSize(14)
.fontColor('#666')
.margin({ top: 10 })
Text() {
Span(`${this.currentPos}`)
.fontSize(28)
.fontWeight(FontWeight.Bold)
Span(this.isAddition ? ' + ' : ' - ')
.fontSize(28)
.fontColor(this.isAddition ? '#4CAF50' : '#F44336')
Span(`${this.moveSteps}`)
.fontSize(28)
.fontWeight(FontWeight.Bold)
Span(' = ')
.fontSize(24)
.fontColor('#999')
Span(`${this.isAddition ? this.currentPos + this.moveSteps : this.currentPos - this.moveSteps}`)
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#FF9800')
}
}
.width('100%')
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(12)
// ================= 交互控制区 =================
Column({ space: 15 }) {
// 符号选择
Row({ space: 15 }) {
Button('加法 (+)')
.width('45%')
.backgroundColor(this.isAddition ? '#4CAF50' : '#CCCCCC')
.onClick(() => {
this.isAddition = true
})
Button('减法 (-)')
.width('45%')
.backgroundColor(this.isAddition ? '#CCCCCC' : '#F44336')
.onClick(() => {
this.isAddition = false
})
}
.width('100%')
// 步数选择
Text('选择移动步数:').fontSize(16)
Grid() {
ForEach([1, 2, 3, 4, 5], (num: number) => {
GridItem() {
Button(num.toString())
.width(50)
.height(40)
.backgroundColor(this.moveSteps === num ? '#2196F3' : '#EEEEEE')
.fontColor(this.moveSteps === num ? '#FFFFFF' : '#333333')
.onClick(() => { this.moveSteps = num })
}
})
}
.columnsTemplate('1fr 1fr 1fr 1fr 1fr')
.width('100%')
// 执行按钮
Button('开始移动')
.width('100%')
.height(50)
.fontSize(20)
.backgroundColor('#FF9800')
.onClick(() => this.executeMove())
}
.width('100%')
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(12)
// 重置按钮
Button('重置位置')
.width('100%')
.height(40)
.backgroundColor('#9E9E9E')
.onClick(() => {
this.currentPos = 0
})
// 提示信息
Text('提示:负数运算时,小人会向左越过原点(0)。')
.fontSize(12)
.fontColor('#999')
.margin({ top: 10 })
}
.width('100%')
.padding(20)
.backgroundColor('#F5F5F5')
}
.width('100%')
.height('100%')
}
// 绘制数轴
private drawNumberLine() {
const ctx = this.context
const width = 360 // 画布宽度
const height = 150 // 画布高度
const centerX = width / 2
const centerY = height / 2 + 20
// 清空画布
ctx.clearRect(0, 0, width, height)
// 绘制数轴线
ctx.strokeStyle = '#333333'
ctx.lineWidth = 2
ctx.beginPath()
ctx.moveTo(20, centerY)
ctx.lineTo(width - 20, centerY)
ctx.stroke()
// 绘制箭头
ctx.beginPath()
ctx.moveTo(width - 20, centerY)
ctx.lineTo(width - 30, centerY - 5)
ctx.lineTo(width - 30, centerY + 5)
ctx.closePath()
ctx.fillStyle = '#333333'
ctx.fill()
// 绘制刻度和数字
ctx.font = '14px sans-serif'
ctx.textAlign = 'center'
ctx.fillStyle = '#333333'
for (let i = this.rangeMin; i <= this.rangeMax; i++) {
const x = centerX + i * this.unitWidth
// 绘制刻度线
ctx.beginPath()
ctx.moveTo(x, centerY - 8)
ctx.lineTo(x, centerY + 8)
ctx.strokeStyle = i === 0 ? '#FF5722' : '#333333'
ctx.lineWidth = i === 0 ? 3 : 2
ctx.stroke()
// 绘制数字
ctx.fillStyle = i === 0 ? '#FF5722' : '#333333'
ctx.font = i === 0 ? 'bold 16px sans-serif' : '14px sans-serif'
ctx.fillText(i.toString(), x, centerY + 30)
}
// 绘制原点标记
ctx.beginPath()
ctx.arc(centerX, centerY, 4, 0, Math.PI * 2)
ctx.fillStyle = '#FF5722'
ctx.fill()
}
// 执行移动逻辑
private executeMove() {
if (this.isMoving) return
let targetPos = this.currentPos
if (this.isAddition) {
targetPos += this.moveSteps
} else {
targetPos -= this.moveSteps
}
// 边界检查
if (targetPos < this.rangeMin || targetPos > this.rangeMax) {
// 超出范围不执行
return
}
this.isMoving = true
// 更新状态,触发动画
this.currentPos = targetPos
// 动画结束后重置状态
setTimeout(() => {
this.isMoving = false
}, 600)
}
}
更多推荐



所有评论(0)