鴻蒙學(xué)習(xí)筆記二十六:折線圖

效果圖
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)
    }
  }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容