效果圖

9f4b8a789f3d84bbbf94aaf4d53844e4.jpg
話不多說,直接上代碼
// 定義數(shù)據(jù)項接口(可選,用于類型約束)
interface DataItem {
x: string; // 月份
y: number; // 數(shù)值
}
@Entry
@Component
export struct LineChartExample {
private renderingSettings: RenderingContextSettings = new RenderingContextSettings(true)
private canvas: CanvasRenderingContext2D =
new CanvasRenderingContext2D(this.renderingSettings) // CanvasRenderingContext2D對象
// 畫布尺寸配置
private canvasWidth: number = 360
private canvasHeight: number = 250
private top = 30
private right = 30
private bottom = 30
private left = 30
// 鴻蒙中定義私有數(shù)據(jù)數(shù)組
private data: DataItem[] = [
{ x: '1月', y: 120 },
{ x: '2月', y: 190 },
{ x: '3月', y: 150 },
{ x: '4月', y: 240 },
{ x: '5月', y: 180 },
{ x: '6月', y: 300 }
]
build() {
Column() {
Text('月度數(shù)據(jù)趨勢圖')
.fontSize(18)
.margin({ top: 50 })
Canvas(this.canvas)
.width('90%')
.height(300)
.onReady(() => {
//抗鋸齒的設(shè)置
this.canvas.imageSmoothingEnabled = true
this.canvas.imageSmoothingQuality = 'medium'
this.onCanvasDraw(this.canvas)
})
}
.width('100%')
.padding(10)
}
// 繪制邏輯
private onCanvasDraw(canvas: CanvasRenderingContext2D) {
if (this.canvasWidth === 0 || this.canvasHeight === 0) {
return
}
// 清除畫布
canvas.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
// 計算繪圖區(qū)域
const plotWidth = this.canvasWidth - this.left - this.right
const plotHeight = this.canvasHeight - this.top - this.bottom
// 繪制坐標(biāo)軸
this.drawAxis(canvas, plotWidth, plotHeight)
// 繪制網(wǎng)格線
this.drawGrid(canvas, plotWidth, plotHeight)
// 繪制折線
this.drawLine(canvas, plotWidth, plotHeight)
// 繪制數(shù)據(jù)點
this.drawDataPoints(canvas, plotWidth, plotHeight)
// 繪制數(shù)據(jù)標(biāo)簽
this.drawDataLabels(canvas, plotWidth, plotHeight)
}
// 繪制坐標(biāo)軸
private drawAxis(canvas: CanvasRenderingContext2D, plotWidth: number, plotHeight: number) {
canvas.beginPath()
// X軸
canvas.moveTo(this.left, this.top + plotHeight)
canvas.lineTo(this.left + plotWidth, this.top + plotHeight)
// Y軸
canvas.moveTo(this.left, this.top)
canvas.lineTo(this.left, this.top + plotHeight)
canvas.strokeStyle = '#666'
canvas.stroke()
}
// 繪制網(wǎng)格線
private drawGrid(canvas: CanvasRenderingContext2D, plotWidth: number, plotHeight: number) {
const xStep = plotWidth / (this.data.length - 1)
const yStep = plotHeight / 4 // 4條水平線
canvas.strokeStyle = '#eee'
canvas.lineWidth = 1
// 垂直線
for (let i = 0; i < this.data.length; i++) {
const x = this.left + i * xStep
canvas.beginPath()
canvas.moveTo(x, this.top)
canvas.lineTo(x, this.top + plotHeight)
canvas.stroke()
}
// 水平線
for (let i = 0; i <= 4; i++) {
const y = this.top + i * yStep
canvas.beginPath()
canvas.moveTo(this.left, y)
canvas.lineTo(this.left + plotWidth, y)
canvas.stroke()
}
}
// 繪制折線
private drawLine(canvas: CanvasRenderingContext2D, plotWidth: number, plotHeight: number) {
const xStep = plotWidth / (this.data.length - 1)
const maxValue = Math.max(...this.data.map(item => item.y))
const valueRatio = plotHeight / maxValue
canvas.beginPath()
this.data.forEach((item, index) => {
const x = this.left + index * xStep
// Y軸坐標(biāo)需要反轉(zhuǎn)(因為Canvas原點在左上角)
const y = this.top + plotHeight - item.y * valueRatio
if (index === 0) {
canvas.moveTo(x, y)
} else {
canvas.lineTo(x, y)
}
})
canvas.strokeStyle = '#007aff'
canvas.lineWidth = 2
canvas.stroke()
}
// 繪制數(shù)據(jù)點
private drawDataPoints(canvas: CanvasRenderingContext2D, plotWidth: number, plotHeight: number) {
const xStep = plotWidth / (this.data.length - 1)
const maxValue = Math.max(...this.data.map(item => item.y))
const valueRatio = plotHeight / maxValue
this.data.forEach((item, index) => {
const x = this.left + index * xStep
const y = this.top + plotHeight - item.y * valueRatio
canvas.beginPath()
canvas.arc(x, y, 4, 0, 2 * Math.PI)
canvas.fillStyle = '#007aff'
canvas.fill()
// 白色內(nèi)圈
canvas.beginPath()
canvas.arc(x, y, 2, 0, 2 * Math.PI)
canvas.fillStyle = '#fff'
canvas.fill()
})
}
// 繪制數(shù)據(jù)標(biāo)簽
private drawDataLabels(canvas: CanvasRenderingContext2D, plotWidth: number, plotHeight: number) {
const xStep = plotWidth / (this.data.length - 1)
const maxValue = Math.max(...this.data.map(item => item.y))
const valueRatio = plotHeight / maxValue
// X軸標(biāo)簽
this.data.forEach((item, index) => {
const x = this.left + index * xStep
canvas.fillStyle = '#000'
canvas.font = '15vp sans-serif'
canvas.textAlign = 'center'
canvas.fillText(item.x, x, this.top + plotHeight + 20)
})
// Y軸標(biāo)簽
for (let i = 0; i <= 4; i++) {
const value = Math.round(maxValue * (1 - i / 4))
const y = this.top + i * (plotHeight / 4)
canvas.fillStyle = '#000'
canvas.font = '15vp sans-serif'
canvas.textAlign = 'right'
canvas.fillText(value.toString(), this.left - 5, y + 4)
}
}
}