Harmonyos应用实例227:平面向量的坐标运算
·
9. 平面向量的坐标运算
功能简介:支持平面向量的坐标表示、加减、数乘、点积运算,通过坐标系可视化展示向量运算过程。计算向量的模、夹角、单位向量,帮助学生理解平面向量的坐标运算和几何意义。
ArkTS代码:
@Entry
@Component
struct VectorCoordinate {
@State private vector1: number[] = [1, 2]
@State private vector2: number[] = [3, 4]
@State private scalar: number = 2
@State private operation: string = 'add'
@State private result: string = ''
@State private vector1Modulus: number = Math.sqrt(5)
@State private vector2Modulus: number = Math.sqrt(25)
@State private angle: number = 0
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
build() {
Column() {
Text('🔄 平面向量坐标运算')
.fontSize(24).fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
Canvas(this.context)
.width(400).height(300)
.backgroundColor('#f5f5f5')
.onReady(() => this.drawVectors())
Text('运算类型')
.fontSize(18).fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 10 })
Row() {
Button('加法')
.width(80)
.onClick(() => { this.operation = 'add'; this.calculate() })
Button('减法')
.width(80)
.onClick(() => { this.operation = 'subtract'; this.calculate() })
Button('数乘')
.width(80)
.onClick(() => { this.operation = 'scalar'; this.calculate() })
Button('点积')
.width(80)
.onClick(() => { this.operation = 'dot'; this.calculate() })
}
.margin({ bottom: 20 })
Text('向量1 (x, y): ')
.fontSize(16).fontWeight(FontWeight.Bold)
.margin({ bottom: 5 })
Row() {
TextInput({
placeholder: 'x',
text: this.vector1[0].toString()
})
.width(80)
.onChange((v: string) => {
this.vector1[0] = parseFloat(v) || 0
this.calculate()
})
TextInput({
placeholder: 'y',
text: this.vector1[1].toString()
})
.width(80)
.onChange((v: string) => {
this.vector1[1] = parseFloat(v) || 0
this.calculate()
})
}
.margin({ bottom: 15 })
Text('向量2 (x, y): ')
.fontSize(16).fontWeight(FontWeight.Bold)
.margin({ bottom: 5 })
Row() {
TextInput({
placeholder: 'x',
text: this.vector2[0].toString()
})
.width(80)
.onChange((v: string) => {
this.vector2[0] = parseFloat(v) || 0
this.calculate()
})
TextInput({
placeholder: 'y',
text: this.vector2[1].toString()
})
.width(80)
.onChange((v: string) => {
this.vector2[1] = parseFloat(v) || 0
this.calculate()
})
}
.margin({ bottom: 15 })
Text('标量: ')
.fontSize(16).fontWeight(FontWeight.Bold)
.margin({ bottom: 5 })
Row() {
TextInput({
placeholder: '标量',
text: this.scalar.toString()
})
.width(100)
.onChange((v: string) => {
this.scalar = parseFloat(v) || 0
if (this.operation === 'scalar') {
this.calculate()
}
})
}
.margin({ bottom: 20 })
Text('运算结果')
.fontSize(18).fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
Text(this.result)
.fontSize(16).fontColor('#2196F3')
Text('向量属性')
.fontSize(18).fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 10 })
Text(`向量1的模: ${this.vector1Modulus.toFixed(2)}`)
.fontSize(14).fontColor('#666')
Text(`向量2的模: ${this.vector2Modulus.toFixed(2)}`)
.fontSize(14).fontColor('#666')
Text(`两向量夹角: ${this.angle.toFixed(2)}°`)
.fontSize(14).fontColor('#666')
Text('向量运算的几何意义')
.fontSize(18).fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 10 })
Text('加法: 平行四边形法则')
.fontSize(14).fontColor('#666')
Text('减法: 三角形法则')
.fontSize(14).fontColor('#666')
Text('数乘: 长度缩放,方向不变或相反')
.fontSize(14).fontColor('#666')
Text('点积: 一个向量在另一个向量上的投影')
.fontSize(14).fontColor('#666')
}
.padding(20)
}
private calculate() {
// 计算向量的模
this.vector1Modulus = Math.sqrt(this.vector1[0] ** 2 + this.vector1[1] ** 2)
this.vector2Modulus = Math.sqrt(this.vector2[0] ** 2 + this.vector2[1] ** 2)
// 计算两向量夹角
const dotProduct = this.vector1[0] * this.vector2[0] + this.vector1[1] * this.vector2[1]
if (this.vector1Modulus > 0 && this.vector2Modulus > 0) {
const cosTheta = dotProduct / (this.vector1Modulus * this.vector2Modulus)
this.angle = Math.acos(Math.max(-1, Math.min(1, cosTheta))) * 180 / Math.PI
} else {
this.angle = 0
}
// 执行运算
switch (this.operation) {
case 'add':
const sum = [this.vector1[0] + this.vector2[0], this.vector1[1] + this.vector2[1]]
this.result = `和向量: (${sum[0].toFixed(2)}, ${sum[1].toFixed(2)})`
break
case 'subtract':
const diff = [this.vector1[0] - this.vector2[0], this.vector1[1] - this.vector2[1]]
this.result = `差向量: (${diff[0].toFixed(2)}, ${diff[1].toFixed(2)})`
break
case 'scalar':
const scaled = [this.vector1[0] * this.scalar, this.vector1[1] * this.scalar]
this.result = `数乘结果: (${scaled[0].toFixed(2)}, ${scaled[1].toFixed(2)})`
break
case 'dot':
const dot = this.vector1[0] * this.vector2[0] + this.vector1[1] * this.vector2[1]
this.result = `点积: ${dot.toFixed(2)}`
break
}
this.drawVectors()
}
private drawVectors() {
const ctx = this.context
const width = 400
const height = 300
const centerX = width / 2
const centerY = height / 2
const scale = 30
// 清空画布
ctx.clearRect(0, 0, width, height)
// 绘制坐标系
ctx.beginPath()
ctx.moveTo(50, centerY)
ctx.lineTo(width - 50, centerY)
ctx.moveTo(centerX, 50)
ctx.lineTo(centerX, height - 50)
ctx.strokeStyle = '#000'
ctx.lineWidth = 1
ctx.stroke()
// 绘制刻度
for (let i = -5; i <= 5; i++) {
const x = centerX + i * scale
ctx.beginPath()
ctx.moveTo(x, centerY - 5)
ctx.lineTo(x, centerY + 5)
ctx.stroke()
ctx.font = '12px Arial'
ctx.fillStyle = '#000'
ctx.fillText(i.toString(), x - 5, centerY + 15)
}
for (let i = -5; i <= 5; i++) {
const y = centerY - i * scale
ctx.beginPath()
ctx.moveTo(centerX - 5, y)
ctx.lineTo(centerX + 5, y)
ctx.stroke()
ctx.font = '12px Arial'
ctx.fillStyle = '#000'
ctx.fillText(i.toString(), centerX - 15, y + 4)
}
// 绘制向量1
const x1 = centerX + this.vector1[0] * scale
const y1 = centerY - this.vector1[1] * scale
ctx.beginPath()
ctx.moveTo(centerX, centerY)
ctx.lineTo(x1, y1)
ctx.strokeStyle = '#2196F3'
ctx.lineWidth = 2
ctx.stroke()
// 绘制向量1的箭头
this.drawArrow(ctx, centerX, centerY, x1, y1, '#2196F3')
// 绘制向量2
const x2 = centerX + this.vector2[0] * scale
const y2 = centerY - this.vector2[1] * scale
ctx.beginPath()
ctx.moveTo(centerX, centerY)
ctx.lineTo(x2, y2)
ctx.strokeStyle = '#4CAF50'
ctx.lineWidth = 2
ctx.stroke()
// 绘制向量2的箭头
this.drawArrow(ctx, centerX, centerY, x2, y2, '#4CAF50')
// 绘制运算结果
switch (this.operation) {
case 'add':
const sumX = centerX + (this.vector1[0] + this.vector2[0]) * scale
const sumY = centerY - (this.vector1[1] + this.vector2[1]) * scale
// 绘制平行四边形法则
ctx.beginPath()
ctx.moveTo(x1, y1)
ctx.lineTo(sumX, sumY)
ctx.lineTo(x2, y2)
ctx.strokeStyle = '#FF9800'
ctx.lineWidth = 1
ctx.setLineDash([5, 5])
ctx.stroke()
ctx.setLineDash([])
// 绘制和向量
ctx.beginPath()
ctx.moveTo(centerX, centerY)
ctx.lineTo(sumX, sumY)
ctx.strokeStyle = '#FF9800'
ctx.lineWidth = 2
ctx.stroke()
// 绘制和向量的箭头
this.drawArrow(ctx, centerX, centerY, sumX, sumY, '#FF9800')
break
case 'subtract':
const diffX = centerX + (this.vector1[0] - this.vector2[0]) * scale
const diffY = centerY - (this.vector1[1] - this.vector2[1]) * scale
// 绘制三角形法则
ctx.beginPath()
ctx.moveTo(x2, y2)
ctx.lineTo(diffX, diffY)
ctx.strokeStyle = '#9C27B0'
ctx.lineWidth = 1
ctx.setLineDash([5, 5])
ctx.stroke()
ctx.setLineDash([])
// 绘制差向量
ctx.beginPath()
ctx.moveTo(centerX, centerY)
ctx.lineTo(diffX, diffY)
ctx.strokeStyle = '#9C27B0'
ctx.lineWidth = 2
ctx.stroke()
// 绘制差向量的箭头
this.drawArrow(ctx, centerX, centerY, diffX, diffY, '#9C27B0')
break
case 'scalar':
const scaledX = centerX + (this.vector1[0] * this.scalar) * scale
const scaledY = centerY - (this.vector1[1] * this.scalar) * scale
// 绘制数乘结果
ctx.beginPath()
ctx.moveTo(centerX, centerY)
ctx.lineTo(scaledX, scaledY)
ctx.strokeStyle = '#FF5722'
ctx.lineWidth = 2
ctx.stroke()
// 绘制数乘结果的箭头
this.drawArrow(ctx, centerX, centerY, scaledX, scaledY, '#FF5722')
break
}
// 绘制标签
ctx.font = '12px Arial'
ctx.fillStyle = '#2196F3'
ctx.fillText('v₁', x1 + 10, y1 - 10)
ctx.fillStyle = '#4CAF50'
ctx.fillText('v₂', x2 + 10, y2 - 10)
}
private drawArrow(ctx: CanvasRenderingContext2D, fromX: number, fromY: number, toX: number, toY: number, color: string) {
const headLength = 10
const angle = Math.atan2(toY - fromY, toX - fromX)
ctx.beginPath()
ctx.moveTo(toX, toY)
ctx.lineTo(
toX - headLength * Math.cos(angle - Math.PI / 6),
toY - headLength * Math.sin(angle - Math.PI / 6)
)
ctx.lineTo(
toX - headLength * Math.cos(angle + Math.PI / 6),
toY - headLength * Math.sin(angle + Math.PI / 6)
)
ctx.closePath()
ctx.fillStyle = color
ctx.fill()
}
}
更多推荐



所有评论(0)