HARMONYOS应用实例260:函数交点求法
·
- 函数交点求法
- 功能:同时绘制两个函数图像,高亮显示交点坐标,演示方程组的几何意义。同时绘制两个函数图像(一次函数、二次函数、反比例函数)
支持三种函数类型的组合:一次函数与一次函数、一次函数与二次函数、一次函数与反比例函数、二次函数与二次函数、二次函数与反比例函数
实时计算并高亮显示交点坐标
演示方程组的几何意义
可调节函数系数,观察交点变化
支持显示选项控制(网格、坐标轴、交点)
// 函数交点求法
// 功能:同时绘制两个函数图像,高亮显示交点坐标,演示方程组的几何意义
// 交点数据接口
interface IntersectionPoint {
x: number;
y: number;
}
// 函数类型
interface FunctionConfig {
type: 'linear' | 'quadratic' | 'inverse';
a: number;
b: number;
c: number;
color: string;
name: string;
}
@Entry
@Component
struct FunctionIntersection {
@State canvasWidth: number = 350;
@State canvasHeight: number = 350;
@State showGrid: boolean = true;
@State showAxes: boolean = true;
@State showIntersections: boolean = true;
@State intersections: IntersectionPoint[] = [];
@State function1: FunctionConfig = {
type: 'linear',
a: 1,
b: 0,
c: 0,
color: '#2196F3',
name: 'f(x)'
};
@State function2: FunctionConfig = {
type: 'quadratic',
a: 1,
b: 0,
c: -4,
color: '#4CAF50',
name: 'g(x)'
};
build() {
Column({ space: 15 }) {
Text('函数交点求法')
.fontSize(26)
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center)
Column() {
Text('功能介绍')
.fontSize(18)
.fontWeight(FontWeight.Medium)
Text('同时绘制两个函数图像,高亮显示交点坐标,演示方程组的几何意义')
.fontSize(14)
.fontColor('#666666')
.textAlign(TextAlign.Center)
}
.width('100%')
.backgroundColor('#E3F2FD')
.borderRadius(10)
.padding(15)
Column({ space: 10 }) {
Text('函数图像')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
Canvas(this.canvasContext)
.width(this.canvasWidth)
.height(this.canvasHeight)
.backgroundColor('#FFFFFF')
.border({ width: 2, color: '#333' })
.borderRadius(10)
.onReady(() => {
this.drawGraph()
})
}
.width('100%')
.padding(10)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 15 }) {
Text('函数1设置')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
Row({ space: 10 }) {
Button('一次函数')
.width('31%')
.height(40)
.fontSize(14)
.backgroundColor(this.function1.type === 'linear' ? '#2196F3' : '#E0E0E0')
.onClick(() => {
this.function1.type = 'linear'
this.updateIntersections()
})
Button('二次函数')
.width('31%')
.height(40)
.fontSize(14)
.backgroundColor(this.function1.type === 'quadratic' ? '#2196F3' : '#E0E0E0')
.onClick(() => {
this.function1.type = 'quadratic'
this.updateIntersections()
})
Button('反比例函数')
.width('31%')
.height(40)
.fontSize(14)
.backgroundColor(this.function1.type === 'inverse' ? '#2196F3' : '#E0E0E0')
.onClick(() => {
this.function1.type = 'inverse'
this.updateIntersections()
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Column({ space: 8 }) {
Text(`a值: ${this.function1.a.toFixed(2)}`)
.fontSize(14)
.fontColor('#666666')
Slider({
value: this.function1.a,
min: -2,
max: 2,
step: 0.1
})
.width('100%')
.blockColor('#2196F3')
.trackColor('#E0E0E0')
.selectedColor('#2196F3')
.onChange((value: number) => {
if (value === 0 && (this.function1.type === 'inverse' || this.function1.type === 'quadratic')) {
value = 0.1
}
this.function1.a = value
this.updateIntersections()
})
}
.width('100%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
Column({ space: 8 }) {
Text(`b值: ${this.function1.b.toFixed(2)}`)
.fontSize(14)
.fontColor('#666666')
Slider({
value: this.function1.b,
min: -10,
max: 10,
step: 0.1
})
.width('100%')
.blockColor('#2196F3')
.trackColor('#E0E0E0')
.selectedColor('#2196F3')
.onChange((value: number) => {
this.function1.b = value
this.updateIntersections()
})
}
.width('100%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
Column({ space: 8 }) {
Text(`c值: ${this.function1.c.toFixed(2)}`)
.fontSize(14)
.fontColor('#666666')
Slider({
value: this.function1.c,
min: -10,
max: 10,
step: 0.1
})
.width('100%')
.blockColor('#2196F3')
.trackColor('#E0E0E0')
.selectedColor('#2196F3')
.onChange((value: number) => {
this.function1.c = value
this.updateIntersections()
})
}
.width('100%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
}
.width('90%')
.padding(10)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 15 }) {
Text('函数2设置')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
Row({ space: 10 }) {
Button('一次函数')
.width('31%')
.height(40)
.fontSize(14)
.backgroundColor(this.function2.type === 'linear' ? '#4CAF50' : '#E0E0E0')
.onClick(() => {
this.function2.type = 'linear'
this.updateIntersections()
})
Button('二次函数')
.width('31%')
.height(40)
.fontSize(14)
.backgroundColor(this.function2.type === 'quadratic' ? '#4CAF50' : '#E0E0E0')
.onClick(() => {
this.function2.type = 'quadratic'
this.updateIntersections()
})
Button('反比例函数')
.width('31%')
.height(40)
.fontSize(14)
.backgroundColor(this.function2.type === 'inverse' ? '#4CAF50' : '#E0E0E0')
.onClick(() => {
this.function2.type = 'inverse'
this.updateIntersections()
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Column({ space: 8 }) {
Text(`a值: ${this.function2.a.toFixed(2)}`)
.fontSize(14)
.fontColor('#666666')
Slider({
value: this.function2.a,
min: -2,
max: 2,
step: 0.1
})
.width('100%')
.blockColor('#4CAF50')
.trackColor('#E0E0E0')
.selectedColor('#4CAF50')
.onChange((value: number) => {
if (value === 0 && (this.function2.type === 'inverse' || this.function2.type === 'quadratic')) {
value = 0.1
}
this.function2.a = value
this.updateIntersections()
})
}
.width('100%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
Column({ space: 8 }) {
Text(`b值: ${this.function2.b.toFixed(2)}`)
.fontSize(14)
.fontColor('#666666')
Slider({
value: this.function2.b,
min: -10,
max: 10,
step: 0.1
})
.width('100%')
.blockColor('#4CAF50')
.trackColor('#E0E0E0')
.selectedColor('#4CAF50')
.onChange((value: number) => {
this.function2.b = value
this.updateIntersections()
})
}
.width('100%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
Column({ space: 8 }) {
Text(`c值: ${this.function2.c.toFixed(2)}`)
.fontSize(14)
.fontColor('#666666')
Slider({
value: this.function2.c,
min: -10,
max: 10,
step: 0.1
})
.width('100%')
.blockColor('#4CAF50')
.trackColor('#E0E0E0')
.selectedColor('#4CAF50')
.onChange((value: number) => {
this.function2.c = value
this.updateIntersections()
})
}
.width('100%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
}
.width('90%')
.padding(10)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 10 }) {
Text('交点分析')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
if (this.intersections.length > 0) {
Column({ space: 8 }) {
Text(`找到 ${this.intersections.length} 个交点:`)
.fontSize(16)
.fontColor('#2196F3')
ForEach(this.intersections, (point: IntersectionPoint, index: number) => {
Text(`交点 ${index + 1}: (${point.x.toFixed(2)}, ${point.y.toFixed(2)})`)
.fontSize(14)
.fontColor('#4CAF50')
})
Text('几何意义: 交点坐标是方程组的解')
.fontSize(14)
.fontColor('#666666')
}
.width('100%')
.padding(15)
.backgroundColor('#E8F5E9')
.borderRadius(10)
} else {
Column() {
Text('没有找到交点')
.fontSize(16)
.fontColor('#F44336')
Text('尝试调整函数参数以获得交点')
.fontSize(14)
.fontColor('#666666')
}
.width('100%')
.padding(15)
.backgroundColor('#FFEBEE')
.borderRadius(10)
}
}
.width('100%')
.padding(10)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 8 }) {
Text('显示选项')
.fontSize(16)
.fontWeight(FontWeight.Medium)
Row({ space: 10 }) {
Row({ space: 5 }) {
Text('显示网格')
.fontSize(14)
Text(this.showGrid ? '✓' : '✗')
.fontSize(14)
.fontColor(this.showGrid ? '#4CAF50' : '#F44336')
}
.onClick(() => {
this.showGrid = !this.showGrid
this.drawGraph()
})
Row({ space: 5 }) {
Text('显示坐标轴')
.fontSize(14)
Text(this.showAxes ? '✓' : '✗')
.fontSize(14)
.fontColor(this.showAxes ? '#4CAF50' : '#F44336')
}
.onClick(() => {
this.showAxes = !this.showAxes
this.drawGraph()
})
Row({ space: 5 }) {
Text('显示交点')
.fontSize(14)
Text(this.showIntersections ? '✓' : '✗')
.fontSize(14)
.fontColor(this.showIntersections ? '#4CAF50' : '#F44336')
}
.onClick(() => {
this.showIntersections = !this.showIntersections
this.drawGraph()
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
}
.width('90%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
Column({ space: 8 }) {
Text('使用说明')
.fontSize(16)
.fontWeight(FontWeight.Bold)
Text('• 选择两个函数的类型(一次、二次、反比例)')
.fontSize(14)
.fontColor('#666666')
Text('• 调整函数的系数a、b、c')
.fontSize(14)
.fontColor('#666666')
Text('• 观察函数图像的交点')
.fontSize(14)
.fontColor('#666666')
Text('• 交点坐标即为方程组的解')
.fontSize(14)
.fontColor('#666666')
}
.width('95%')
.padding(12)
.backgroundColor('#FFF3E0')
.borderRadius(10)
Row({ space: 10 }) {
Button('重置')
.width('100%')
.height(50)
.fontSize(16)
.backgroundColor('#FF9800')
.onClick(() => {
this.reset()
})
}
.width('100%')
}
.width('100%')
.height('100%')
.padding(10)
.justifyContent(FlexAlign.Start)
}
private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D();
private updateIntersections() {
this.calculateIntersections()
this.drawGraph()
}
private calculateIntersections() {
this.intersections = []
if (this.function1.type === 'linear' && this.function2.type === 'linear') {
this.calculateLinearLinearIntersection()
} else if (this.function1.type === 'linear' && this.function2.type === 'quadratic') {
this.calculateLinearQuadraticIntersection()
} else if (this.function1.type === 'quadratic' && this.function2.type === 'linear') {
this.calculateLinearQuadraticIntersection(true)
} else if (this.function1.type === 'linear' && this.function2.type === 'inverse') {
this.calculateLinearInverseIntersection()
} else if (this.function1.type === 'inverse' && this.function2.type === 'linear') {
this.calculateLinearInverseIntersection(true)
} else if (this.function1.type === 'quadratic' && this.function2.type === 'inverse') {
this.calculateQuadraticInverseIntersection()
} else if (this.function1.type === 'inverse' && this.function2.type === 'quadratic') {
this.calculateQuadraticInverseIntersection(true)
} else if (this.function1.type === 'quadratic' && this.function2.type === 'quadratic') {
this.calculateQuadraticQuadraticIntersection()
}
}
private calculateLinearLinearIntersection() {
const a1 = this.function1.a
const b1 = this.function1.b
const c1 = this.function1.c
const a2 = this.function2.a
const b2 = this.function2.b
const c2 = this.function2.c
const denominator = a1 * b2 - a2 * b1
if (denominator !== 0) {
const x = (b1 * c2 - b2 * c1) / denominator
const y = (a2 * c1 - a1 * c2) / denominator
const point: IntersectionPoint = {
x: x,
y: y
}
this.intersections.push(point)
}
}
private calculateLinearQuadraticIntersection(swap: boolean = false) {
const linear = swap ? this.function2 : this.function1
const quadratic = swap ? this.function1 : this.function2
const a = quadratic.a
const b = quadratic.b - linear.a
const c = quadratic.c - linear.b
const discriminant = b * b - 4 * a * c
if (discriminant >= 0) {
const x1 = (-b + Math.sqrt(discriminant)) / (2 * a)
const y1 = linear.a * x1 + linear.b
const point1: IntersectionPoint = {
x: x1,
y: y1
}
this.intersections.push(point1)
if (discriminant > 0) {
const x2 = (-b - Math.sqrt(discriminant)) / (2 * a)
const y2 = linear.a * x2 + linear.b
const point2: IntersectionPoint = {
x: x2,
y: y2
}
this.intersections.push(point2)
}
}
}
private calculateLinearInverseIntersection(swap: boolean = false) {
const linear = swap ? this.function2 : this.function1
const inverse = swap ? this.function1 : this.function2
const a = linear.a
const b = linear.b
const k = inverse.a
const quadraticA = a
const quadraticB = b
const quadraticC = -k
const discriminant = b * b + 4 * a * k
if (discriminant >= 0) {
const x1 = (-b + Math.sqrt(discriminant)) / (2 * a)
if (x1 !== 0) {
const y1 = k / x1
const point1: IntersectionPoint = {
x: x1,
y: y1
}
this.intersections.push(point1)
}
if (discriminant > 0) {
const x2 = (-b - Math.sqrt(discriminant)) / (2 * a)
if (x2 !== 0) {
const y2 = k / x2
const point2: IntersectionPoint = {
x: x2,
y: y2
}
this.intersections.push(point2)
}
}
}
}
private calculateQuadraticInverseIntersection(swap: boolean = false) {
const quadratic = swap ? this.function2 : this.function1
const inverse = swap ? this.function1 : this.function2
const a = quadratic.a
const b = quadratic.b
const c = quadratic.c
const k = inverse.a
for (let x = -10; x <= 10; x += 0.1) {
if (x !== 0) {
const y1 = a * x * x + b * x + c
const y2 = k / x
if (Math.abs(y1 - y2) < 0.1) {
const point: IntersectionPoint = {
x: x,
y: y1
}
this.intersections.push(point)
}
}
}
}
private calculateQuadraticQuadraticIntersection() {
const a1 = this.function1.a
const b1 = this.function1.b
const c1 = this.function1.c
const a2 = this.function2.a
const b2 = this.function2.b
const c2 = this.function2.c
const a = a1 - a2
const b = b1 - b2
const c = c1 - c2
if (a === 0) {
if (b !== 0) {
const x = -c / b
const y = a1 * x * x + b1 * x + c1
const point: IntersectionPoint = {
x: x,
y: y
}
this.intersections.push(point)
}
} else {
const discriminant = b * b - 4 * a * c
if (discriminant >= 0) {
const x1 = (-b + Math.sqrt(discriminant)) / (2 * a)
const y1 = a1 * x1 * x1 + b1 * x1 + c1
const point1: IntersectionPoint = {
x: x1,
y: y1
}
this.intersections.push(point1)
if (discriminant > 0) {
const x2 = (-b - Math.sqrt(discriminant)) / (2 * a)
const y2 = a1 * x2 * x2 + b1 * x2 + c1
const point2: IntersectionPoint = {
x: x2,
y: y2
}
this.intersections.push(point2)
}
}
}
}
private drawGraph() {
const ctx = this.canvasContext
ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
if (this.showGrid) {
this.drawGridLines(ctx)
}
if (this.showAxes) {
this.drawAxes(ctx)
}
this.drawFunction(ctx, this.function1)
this.drawFunction(ctx, this.function2)
if (this.showIntersections) {
this.drawIntersections(ctx)
}
}
private drawGridLines(ctx: CanvasRenderingContext2D) {
ctx.strokeStyle = '#E0E0E0'
ctx.lineWidth = 1
const centerX = this.canvasWidth / 2
const centerY = this.canvasHeight / 2
const scale = 30
for (let i = -5; i <= 5; i++) {
ctx.beginPath()
ctx.moveTo(centerX + i * scale, 0)
ctx.lineTo(centerX + i * scale, this.canvasHeight)
ctx.stroke()
}
for (let i = -5; i <= 5; i++) {
ctx.beginPath()
ctx.moveTo(0, centerY + i * scale)
ctx.lineTo(this.canvasWidth, centerY + i * scale)
ctx.stroke()
}
}
private drawAxes(ctx: CanvasRenderingContext2D) {
const centerX = this.canvasWidth / 2
const centerY = this.canvasHeight / 2
ctx.strokeStyle = '#333'
ctx.lineWidth = 2
ctx.beginPath()
ctx.moveTo(centerX, 0)
ctx.lineTo(centerX, this.canvasHeight)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(0, centerY)
ctx.lineTo(this.canvasWidth, centerY)
ctx.stroke()
ctx.fillStyle = '#333'
ctx.font = '12px sans-serif'
ctx.fillText('x', this.canvasWidth - 15, centerY + 15)
ctx.fillText('y', centerX + 5, 15)
ctx.fillText('O', centerX + 5, centerY + 15)
const scale = 30
for (let i = -5; i <= 5; i++) {
if (i !== 0) {
ctx.fillText(i.toString(), centerX + i * scale - 3, centerY + 20)
ctx.fillText((-i).toString(), centerX - 15, centerY + i * scale + 3)
}
}
}
private drawFunction(ctx: CanvasRenderingContext2D, func: FunctionConfig) {
const centerX = this.canvasWidth / 2
const centerY = this.canvasHeight / 2
const scale = 30
ctx.strokeStyle = func.color
ctx.lineWidth = 3
ctx.beginPath()
for (let x = -5; x <= 5; x += 0.1) {
let y: number
switch (func.type) {
case 'linear':
y = func.a * x + func.b
break
case 'quadratic':
y = func.a * x * x + func.b * x + func.c
break
case 'inverse':
if (x !== 0) {
y = func.a / x
} else {
continue
}
break
}
const pixelX = centerX + x * scale
const pixelY = centerY - y * scale
if (x === -5) {
ctx.moveTo(pixelX, pixelY)
} else {
ctx.lineTo(pixelX, pixelY)
}
}
ctx.stroke()
ctx.fillStyle = func.color
ctx.font = '14px sans-serif'
let equation: string
switch (func.type) {
case 'linear':
equation = `${func.name} = ${func.a.toFixed(2)}x + ${func.b.toFixed(2)}`
break
case 'quadratic':
equation = `${func.name} = ${func.a.toFixed(2)}x² + ${func.b.toFixed(2)}x + ${func.c.toFixed(2)}`
break
case 'inverse':
equation = `${func.name} = ${func.a.toFixed(2)}/x`
break
}
ctx.fillText(equation, 10, func === this.function1 ? 30 : 50)
}
private drawIntersections(ctx: CanvasRenderingContext2D) {
const centerX = this.canvasWidth / 2
const centerY = this.canvasHeight / 2
const scale = 30
this.intersections.forEach((point: IntersectionPoint) => {
const pixelX = centerX + point.x * scale
const pixelY = centerY - point.y * scale
ctx.fillStyle = '#FF9800'
ctx.beginPath()
ctx.arc(pixelX, pixelY, 8, 0, 2 * Math.PI)
ctx.fill()
ctx.strokeStyle = '#FF9800'
ctx.lineWidth = 2
ctx.stroke()
ctx.fillStyle = '#FF9800'
ctx.font = '12px sans-serif'
ctx.fillText(`(${point.x.toFixed(2)}, ${point.y.toFixed(2)})`, pixelX + 10, pixelY - 10)
})
}
private reset() {
this.function1 = {
type: 'linear',
a: 1,
b: 0,
c: 0,
color: '#2196F3',
name: 'f(x)'
}
this.function2 = {
type: 'quadratic',
a: 1,
b: 0,
c: -4,
color: '#4CAF50',
name: 'g(x)'
}
this.showGrid = true
this.showAxes = true
this.showIntersections = true
this.updateIntersections()
}
}
更多推荐


所有评论(0)