鸿蒙学习笔记二十六:折线图

效果图
9f4b8a789f3d84bbbf94aaf4d53844e4.jpg
话不多说,直接上代码
// 定义数据项接口(可选,用于类型约束)
interface DataItem {
  x: string; // 月份
  y: number; // 数值
}

@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
  // 鸿蒙中定义私有数据数组
  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('月度数据趋势图')
        .fontSize(18)
        .margin({ top: 50 })

      Canvas(this.canvas)
        .width('90%')
        .height(300)
        .onReady(() => {
          //抗锯齿的设置
          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)

    // 计算绘图区域
    const plotWidth = this.canvasWidth - this.left - this.right
    const plotHeight = this.canvasHeight - this.top - this.bottom

    // 绘制坐标轴
    this.drawAxis(canvas, plotWidth, plotHeight)

    // 绘制网格线
    this.drawGrid(canvas, plotWidth, plotHeight)

    // 绘制折线
    this.drawLine(canvas, plotWidth, plotHeight)

    // 绘制数据点
    this.drawDataPoints(canvas, plotWidth, plotHeight)

    // 绘制数据标签
    this.drawDataLabels(canvas, plotWidth, plotHeight)
  }

  // 绘制坐标轴
  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()
  }

  // 绘制网格线
  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轴坐标需要反转(因为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()
  }

  // 绘制数据点
  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()

      // 白色内圈
      canvas.beginPath()
      canvas.arc(x, y, 2, 0, 2 * Math.PI)
      canvas.fillStyle = '#fff'
      canvas.fill()
    })
  }

  // 绘制数据标签
  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轴标签
    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轴标签
    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)
    }
  }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。