HarmonyOS鸿蒙Next中制作圆形环状百分比图表

HarmonyOS鸿蒙Next中制作圆形环状百分比图表

CustomPieChart({ unit: ‘%’, money: ‘’ })

当第四项’D’ 值为 0.00时 绘制的圆形效果不对,无法正确显示其他值的占比, 请问该如何修改?

CustomPieChart({
  unit: '%',
  money: ''
})
@Component
export struct CustomPieChart {
  // 获取上下文
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)

  // 饼图数据
  @Prop picChartElements: PicChartElement[] = [
    new PicChartElement('A', 92.51),
    new PicChartElement('B', 5.63),
    new PicChartElement('C', 1.33),
    new PicChartElement('D', 0.00),
    new PicChartElement('E', 0.53)
  ]
  // 圆半径
  @State circle_radius: number = 60
  // 单位
  @State unit: string = ""
  //总资产
  @Prop money: string = ''

  @Prop colors: string[] =
    ["#3c6ef5", "#76a0ff", "#79d4fe", "#926cfd", "#ae5dba", "#c67cd3", "#c43f5e", "#f4a994", "#fe6833", "#efd796"]

  private picValueChanged() {

    let t = 0
    this.picChartElements.forEach((value) => {
      t += value.quantity
    })

    let pa: PicChartElement[] = []
    for (let i = 0; i < this.picChartElements.length; i++) {
      pa.push(this.picChartElements[i])
      pa.push(new PicChartElement('', t / 200))
    }

    let total = 0
    pa.forEach((value) => {
      total += value.quantity
    })

    // 初始化 弧线的终止弧度
    let lastEndAngle = -0.5 * Math.PI
    // 封装饼图数据
    pa.forEach((value, index) => {
      let percent = value.quantity / (total)
      value.percent = percent
      value.beginAngle = lastEndAngle
      value.endAngle = (percent * 2 * Math.PI) + lastEndAngle
      lastEndAngle = value.endAngle

      if (index % 2 !== 0) {
        value.color = '#00000000'
      } else {
        value.color = this.colors[index / 2]
      }
      //value.color = this.colors[index]
      // 返回封装好的对象
      return value
    })

    // this.context.reset()
    pa.forEach((item) => {
      // 创建一个新的控制路径
      this.context.beginPath()
      // 路径从当前点移动到指定点
      this.context.moveTo(this.circle_radius, this.circle_radius)
      // 绘制弧线路径(弧线圆心的x坐标值,弧线圆心的y坐标值,弧线的圆半径,弧线的起始弧度,弧线的终止弧度)
      this.context.arc(this.circle_radius, this.circle_radius, this.circle_radius, item.beginAngle,
        item.endAngle)
      // 指定绘制的填充色
      this.context.fillStyle = item.color
      // 对封闭路径进行填充
      this.context.fill()
    })
  }

  aboutToAppear() {
    // this.picValueChanged()
  }

  build() {
    Row({ space: 10 }) {
      Stack() {
        Canvas(this.context)// 高度为半径2倍
          .height('100%')// 纵横比,宽度和高度一样
          .width('100%')// 画布组件的事件回调,可以在此时进行绘制
          .onReady(() => {
            this.picValueChanged()
          })

        Stack() {
          Circle({ width: this.circle_radius * 2 - 15, height: this.circle_radius * 2 - 15 })
            .stroke('#FFFFFF')
            .fill('#FFFFFF')

          Text(this.money + '\n总资产')
            .textAlign(TextAlign.Center)
            .maxLines(-1)
            .width('100%')
            .height('100%')
        }
        .width(this.circle_radius * 2 - 15)
        .height(this.circle_radius * 2 - 15)

      }
      .width(this.circle_radius * 2)
      .height(this.circle_radius * 2)

      Blank()
        .layoutWeight(1)

      Column() {
        ForEach(this.picChartElements, (item: PicChartElement) => {
          Row({ space: 10 }) {
            // 标注圆点颜色
            Divider()
              .vertical(true)
              .height(10)
              .strokeWidth(5)
              .color(item.color)

            Row() {
              // 标注文本
              Text(item.element).fontSize(12)
                .textAlign(TextAlign.Start)
                .layoutWeight(1)

              // 标注数量
              Text(item.quantity + this.unit).fontSize(12)
                .textAlign(TextAlign.End)
            }
            .layoutWeight(1)
            .justifyContent(FlexAlign.SpaceBetween)
          }
          .width('100%')
          .height(30)
          .alignItems(VerticalAlign.Center)

        })
      }
      .width(130)
    }
    .width('100%')
    .padding({ left: 20, right: 20 })
  }
}

export class PicChartElement {
  element: Resource | string // 显示文本
  quantity: number // 数量
  percent: number = 0 // 百分比
  beginAngle: number = 0 // 弧线的起始弧度
  endAngle: number = 0 // 弧线的终止弧度
  color: string // 颜色

  constructor(element: Resource | string, quantity: number, color: string = '') {
    this.element = element
    this.quantity = quantity
    this.color = color
  }
}

更多关于HarmonyOS鸿蒙Next中制作圆形环状百分比图表的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复
pa push的时候没有过滤0的项

更多关于HarmonyOS鸿蒙Next中制作圆形环状百分比图表的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


包含0的值是必须要过滤的吗? 有的时候不用过滤0显示也是正确的,

在HarmonyOS鸿蒙Next中制作圆形环状百分比图表,可以使用Canvas组件进行绘制。通过Canvas的arc方法,可以绘制圆形路径,并设置起始角度和结束角度以实现环状效果。使用lineWidth属性调整环的宽度,strokeStyle设置颜色。结合requestAnimationFrame实现动态百分比变化。具体实现可参考鸿蒙开发文档中的Canvas API。

问题出在picValueChanged方法中处理0值数据时的逻辑。当某项值为0时,当前代码仍会为它分配一个弧段,这会导致其他部分的占比计算错误。

建议修改picValueChanged方法,在计算前先过滤掉0值的数据项:

private picValueChanged() {
  // 过滤掉0值数据
  const validElements = this.picChartElements.filter(item => item.quantity > 0);
  
  let t = 0
  validElements.forEach((value) => {
    t += value.quantity
  })

  let pa: PicChartElement[] = []
  for (let i = 0; i < validElements.length; i++) {
    pa.push(validElements[i])
    pa.push(new PicChartElement('', t / 200))
  }

  // 剩余逻辑保持不变...
}

这样修改后,当D项值为0时,它会被排除在计算之外,其他各项的占比就能正确显示了。同时记得更新ForEach循环中的数据源为过滤后的validElements,以保证图例显示一致。

这个修改保持了原有环形图的效果,只是不再为0值项分配空间,更符合数据可视化的常规做法。

回到顶部