Harmonyos应用实例220:三角函数图像与性质探索
·
2. 三角函数图像与性质探索
功能简介:通过调整振幅、周期、相位和垂直位移,实时展示正弦、余弦、正切函数的图像变化。支持函数叠加和复合,计算函数的定义域、值域、周期、奇偶性等性质,帮助学生理解三角函数的图像特征和参数影响。
ArkTS代码:
@Entry
@Component
struct TrigonometricExplorer {
@State private functionType: string = 'sin'
@State private amplitude: number = 1
@State private period: number = 2 * Math.PI
@State private phase: number = 0
@State private verticalShift: number = 0
@State private properties: string = ''
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
build() {
Column() {
Text('🔄 三角函数图像探索')
.fontSize(24).fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
Text('函数类型')
.fontSize(18).fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
Row() {
Button('sin')
.width(80)
.onClick(() => { this.functionType = 'sin'; this.updateProperties() })
Button('cos')
.width(80)
.onClick(() => { this.functionType = 'cos'; this.updateProperties() })
Button('tan')
.width(80)
.onClick(() => { this.functionType = 'tan'; this.updateProperties() })
}
Text('参数调整')
.fontSize(18).fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 10 })
Row() {
Text('振幅: ')
.width(60)
Slider({ value: this.amplitude, min: 0.1, max: 3 })
.width(200)
.onChange((val: number) => { this.amplitude = val; this.updateProperties() })
Text(this.amplitude.toFixed(1))
.width(40)
}
.margin({ bottom: 10 })
Row() {
Text('周期: ')
.width(60)
Slider({ value: this.period, min: Math.PI/2, max: 4 * Math.PI, step: 0.1 })
.width(200)
.onChange((val: number) => { this.period = val; this.updateProperties() })
Text((this.period / Math.PI).toFixed(1) + 'π')
.width(60)
}
.margin({ bottom: 10 })
Row() {
Text('相位: ')
.width(60)
Slider({ value: this.phase, min: -Math.PI, max: Math.PI, step: 0.1 })
.width(200)
.onChange((val: number) => { this.phase = val; this.updateProperties() })
Text((this.phase / Math.PI).toFixed(1) + 'π')
.width(60)
}
.margin({ bottom: 10 })
Row() {
Text('垂直位移: ')
.width(80)
Slider({ value: this.verticalShift, min: -2, max: 2, step: 0.1 })
.width(200)
.onChange((val: number) => { this.verticalShift = val; this.updateProperties() })
Text(this.verticalShift.toFixed(1))
.width(40)
}
Canvas(this.context)
.width(400).height(300)
.backgroundColor('#f5f5f5')
.margin({ top: 20 })
.onReady(() => this.drawTrigonometric())
Text('函数性质')
.fontSize(18).fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 10 })
Text(this.properties)
.fontSize(14).fontColor('#666')
Text('函数表达式')
.fontSize(18).fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 10 })
Text(this.getFunctionExpression())
.fontSize(16).fontColor('#2196F3')
}
.padding(20)
}
private updateProperties() {
let domain: string
let range: string
let parity: string
let asymptotes: string = ''
if (this.functionType === 'tan') {
domain = 'x ≠ π/2 + kπ (k为整数)'
range = '(-∞, ∞)'
parity = '奇函数'
asymptotes = '有垂直渐近线: x = π/2 + kπ'
} else if (this.functionType === 'sin') {
domain = '(-∞, ∞)'
range = `[${-this.amplitude + this.verticalShift}, ${this.amplitude + this.verticalShift}]`
parity = '奇函数'
} else { // cos
domain = '(-∞, ∞)'
range = `[${-this.amplitude + this.verticalShift}, ${this.amplitude + this.verticalShift}]`
parity = '偶函数'
}
this.properties = `定义域: ${domain}\n` +
`值域: ${range}\n` +
`周期: ${(this.period / Math.PI).toFixed(1)}π\n` +
`奇偶性: ${parity}\n` +
`${asymptotes}`
this.drawTrigonometric()
}
private getFunctionExpression(): string {
const amplitude = this.amplitude !== 1 ? this.amplitude.toFixed(1) : ''
const phase = this.phase !== 0 ? (this.phase > 0 ? ` + ${(this.phase / Math.PI).toFixed(1)}π` : ` - ${Math.abs(this.phase / Math.PI).toFixed(1)}π`) : ''
const verticalShift = this.verticalShift !== 0 ? (this.verticalShift > 0 ? ` + ${this.verticalShift.toFixed(1)}` : ` - ${Math.abs(this.verticalShift).toFixed(1)}`) : ''
return `${amplitude}${this.functionType}(x${phase})${verticalShift}`
}
private drawTrigonometric() {
const ctx = this.context
const width = 400
const height = 300
const centerX = width / 2
const centerY = height / 2
const scaleX = 30 // x轴缩放因子
const scaleY = 50 // y轴缩放因子
// 清空画布
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()
// 绘制网格
ctx.strokeStyle = '#ddd'
ctx.lineWidth = 0.5
for (let i = 1; i < 6; i++) {
const x = centerX + i * scaleX
ctx.beginPath()
ctx.moveTo(x, 50)
ctx.lineTo(x, height - 50)
ctx.stroke()
const x2 = centerX - i * scaleX
ctx.beginPath()
ctx.moveTo(x2, 50)
ctx.lineTo(x2, height - 50)
ctx.stroke()
const y = centerY + i * scaleY
ctx.beginPath()
ctx.moveTo(50, y)
ctx.lineTo(width - 50, y)
ctx.stroke()
const y2 = centerY - i * scaleY
ctx.beginPath()
ctx.moveTo(50, y2)
ctx.lineTo(width - 50, y2)
ctx.stroke()
}
// 绘制函数图像
ctx.beginPath()
ctx.strokeStyle = '#2196F3'
ctx.lineWidth = 2
for (let x = -Math.PI * 2; x <= Math.PI * 2; x += 0.01) {
let y: number
try {
if (this.functionType === 'sin') {
y = this.amplitude * Math.sin((2 * Math.PI / this.period) * (x - this.phase)) + this.verticalShift
} else if (this.functionType === 'cos') {
y = this.amplitude * Math.cos((2 * Math.PI / this.period) * (x - this.phase)) + this.verticalShift
} else { // tan
// 跳过渐近线附近的点
const tanArg = (2 * Math.PI / this.period) * (x - this.phase)
if (Math.abs(Math.abs(tanArg % Math.PI) - Math.PI/2) < 0.01) {
continue
}
y = this.amplitude * Math.tan(tanArg) + this.verticalShift
}
} catch {
continue
}
if (Math.abs(y) > 5) continue // 限制范围
const canvasX = centerX + x * scaleX
const canvasY = centerY - y * scaleY
if (x === -Math.PI * 2) {
ctx.moveTo(canvasX, canvasY)
} else {
ctx.lineTo(canvasX, canvasY)
}
}
ctx.stroke()
// 绘制渐近线(对于正切函数)
if (this.functionType === 'tan') {
ctx.strokeStyle = '#FF5722'
ctx.lineWidth = 1
ctx.setLineDash([5, 5])
for (let k = -3; k <= 3; k++) {
const asymptoteX = centerX + (Math.PI/2 + k * Math.PI) * scaleX
ctx.beginPath()
ctx.moveTo(asymptoteX, 50)
ctx.lineTo(asymptoteX, height - 50)
ctx.stroke()
}
ctx.setLineDash([])
}
}
}
更多推荐


所有评论(0)