piechart啥意思

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了piechart啥意思相关的知识,希望对你有一定的参考价值。

参考技术A pie chart:n.圆形统计图;饼分图;
例如:The pie chart is divided into two sections: Covered and Not Covered.

扩展资料

  Shows a pie chart with the percentages of the test results that passed and failed.

  显示一个带有通过与失败测试结果百分比的饼状图。

  Drag to add a customizable pie chart to your drawing page.

  拖动可将可自定义的.饼图添加到您的绘图页中。

  For example, a number of statistical data can be showed by the pie chart, histogram.

  如一批统计数据可以分别用饼图、柱状图表示。

ArkUI实战,自定义饼状图组件PieChart

本节笔者带领读者实现一个饼状图 PieChart 组件,该组件是根据笔者之前封装的 MiniCanvas 实现的, PieChart 的最终演示效果如下图所示:

饼状图实现的拆分

根据上图的样式效果,实现一个饼状图,实质就是绘制一个个的实心圆弧加上圆弧对应颜色就搞定了,圆弧的大小是根据饼状的数据分布计算出来的,对应的颜色自己指定就可以了,其次手指点击到饼状图,需要找到对应的饼状块并突出显示,找到饼状块先计算手指点击坐标和圆弧中心的夹角,根据夹角和每个圆弧的大小找到对应的圆弧,找到圆弧后计算圆弧的突出偏移量并重置所有饼状块的圆弧起始值就可以了。

  • 计算夹角
    计算夹角就是计算手指点击饼状图上的坐标 (x, y) 和饼状图的圆心坐标 (centerX, centerY) 之间的顺时针角度,计算方法如下所示:
private getTouchedAngle(centerX: number, centerY, x: number, y: number) 
  var deltaX = x - centerX;
  var deltaY = centerY - y;
  var t = deltaY / Math.sqrt(deltaX * deltaX + deltaY * deltaY);
  var angle = 0;
  if (deltaX > 0) 
    if (deltaY > 0) 
      angle = Math.asin(t);
     else 
      angle = Math.PI * 2 + Math.asin(t);
    
   else if (deltaY > 0) 
    angle = Math.PI - Math.asin(t);
   else 
    angle = Math.PI - Math.asin(t);
  
  return 360 - (angle * 180 / Math.PI) % 360;

  • 找圆弧块
    计算出手指点击位置和圆心的夹角后,遍历每一个饼状块做比较就可以了,代码如下所示:
private getTouchedPieItem(angle: number): PieItem 
  for(var i = 0; i < this.pieItems.length; i++) 
    var item = this.pieItems[i];
    if(item.getStopAngle() < 360) 
      if(angle >= item.getStartAngle() && angle < item.getStopAngle()) 
        return item;
      
     else 
      if(angle >= item.getStartAngle() && angle < 360 || (angle >= 0 && angle < item.getStopAngle() - 360)) 
        return item;
      
    
  
  return null;

  • 计算偏移量
    找到圆弧块后,根据圆弧块的圆弧大小,计算出该圆弧突出后的偏移量,代码如下所示:
private calculateRoteAngle(item: PieItem): number 
  var result = item.getStartAngle() + item.getAngle() / 2 + this.getDirectionAngle();
  if (result >= 360) 
    result -= 360;
  
  if (result <= 180) 
    result = -result;
   else 
    result = 360 - result;
  
  return result;

  • 重置偏移量
    有了目标圆弧块的偏移角度后,重置每一个圆弧块的起始偏移量就可以了,代码如下所示:
private resetStartAngle(angle: number) 
  this.pieItems.forEach((item) => 
    item.setSelected(false);
    item.setStartAngle(item.getStartAngle() + angle);
  );

  • 重新绘制圆弧
    绘制圆弧使用 MiniCanvas 提供的 drawArc() 方法即可,代码如下所示:
drawPieItem() 
  this.pieItems.forEach((item) => 
    this.paint.setColor(item.color);
    var x = this.calculateCenterX(item.isSelected());
    var y = this.calculateCenterY(item.isSelected());
    this.canvas.drawArc(x, y, this.radius, item.getStartAngle(), item.getStopAngle(), this.paint);
  )

饼状图的实现

拆分完饼状图的步骤后,实现起来就方便多了, PieChart 的完整代码如下所示:

import  MiniCanvas, Paint, ICanvas  from './icanvas'

@Entry @Component struct PieChart 

  private delegate: PieChartDelegate;

  build() 
    Column() 
      MiniCanvas(
        attribute: 
          width: this.delegate.calculateWidth(),
          height: this.delegate.calculateHeight(),
          clickListener: (event) => 
            // 根据点击绘制突出的饼状块
            this.delegate.onClicked(event.x, event.y);
          
        ,
        onDraw: (canvas) => 
          // 开始绘制
          this.delegate.setCanvas(canvas);
          this.delegate.drawPieItem();
        
      )
    
    .padding(10)
    .size(width: "100%", height: "100%")
  

  aboutToAppear() 
    // mock测试数据
    var pieItems = PieItem.mock();
    // 初始化delegate
    this.delegate = new PieChartDelegate(pieItems, RotateDirection.BOTTOM);
  


// 定义饼状块的属性,包括角度,起始角度,占比,颜色,是否选中突出
export class PieItem 
  private startAngle: number = 0;
  private rate: number = 0;
  private angle: number = 0;
  private selected: boolean = false;

  constructor(public count: number, public color: string) 
  

  setSelected(selected: boolean) 
    this.selected = selected;
    return this;
  

  isSelected() 
    return this.selected;
  

  setStartAngle(startAngle: number) 
    this.startAngle = startAngle > 360 ? startAngle - 360 : startAngle < 0 ? 360 + startAngle : startAngle;
    return this;
  

  getStartAngle() 
    return this.startAngle;
  

  getStopAngle() 
    return  this.startAngle + this.angle;
  

  setRate(rate: number) 
    this.rate = rate;
    return this;
  

  getRate() 
    return this.rate;
  

  setAngle(angle: number) 
    this.angle = angle;
    return this;
  

  getAngle() 
    return this.angle;
  

  // mock一份测试数据
  static mock(): Array<PieItem> 
    var pieItems = new Array<PieItem>();
    pieItems.push(new PieItem(21, "#6A5ACD"))
    pieItems.push(new PieItem(18, "#20B2AA"))
    pieItems.push(new PieItem(29, "#FFFF00"))
    pieItems.push(new PieItem(12, "#00BBFF"))
    pieItems.push(new PieItem(20, "#DD5C5C"))
    pieItems.push(new PieItem(13, "#8B668B"))
    return pieItems;
  


// 饼状块的突出方向
export enum RotateDirection 
  LEFT,
  TOP,
  RIGHT,
  BOTTOM


// 饼状图绘制的具体实现类
class PieChartDelegate 

  private paint: Paint;
  private canvas: ICanvas;

  constructor(private pieItems: Array<PieItem>, private direction: RotateDirection = RotateDirection.BOTTOM, private offset: number = 10, private radius: number = 80) 
    this.calculateItemAngle();
  

  setPitItems(pieItems: Array<PieItem>) 
    this.pieItems = pieItems;
  

  setCanvas(canvas: ICanvas) 
    this.canvas = canvas;
    this.paint = new Paint();
  

  onClicked(x: number, y: number) 
    if(this.canvas) 
      var touchedAngle = this.getTouchedAngle(this.radius, this.radius, x, y);
      var touchedItem = this.getTouchedPieItem(touchedAngle);
      if(touchedItem) 
        var rotateAngle = this.calculateRoteAngle(touchedItem);
        this.resetStartAngle(rotateAngle);
        touchedItem.setSelected(true)
        this.clearCanvas();
        this.drawPieItem();
      
     else 
      console.warn("canvas invalid!!!")
    
  

  clearCanvas() 
    this.canvas.clear();
  

  drawPieItem() 
    this.pieItems.forEach((item) => 
      this.paint.setColor(item.color);
      var x = this.calculateCenterX(item.isSelected());
      var y = this.calculateCenterY(item.isSelected());
      this.canvas.drawArc(x, y, this.radius, item.getStartAngle(), item.getStopAngle(), this.paint);
    )
  

  calculateWidth(): number 
    if (this.direction == RotateDirection.LEFT || this.direction == RotateDirection.RIGHT) 
      return this.radius * 2 + this.offset;
     else 
      return this.radius * 2;
    
  

  calculateHeight(): number 
    if (this.direction == RotateDirection.TOP || this.direction == RotateDirection.BOTTOM) 
      return this.radius * 2 + this.offset;
     else 
      return this.radius * 2;
    
  

  private calculateCenterX(hint: boolean): number 
    if(this.direction == RotateDirection.LEFT) 
      return hint ? this.radius : this.radius + this.offset;
     else if(this.direction == RotateDirection.TOP) 
      return this.radius;
     else if(this.direction == RotateDirection.RIGHT) 
      return hint ? this.radius + this.offset : this.radius;
     else 
      return this.radius;
    
  

  private calculateCenterY(hint: boolean): number 
    if(this.direction == RotateDirection.LEFT) 
      return this.radius;
     else if(this.direction == RotateDirection.TOP) 
      return hint ? this.radius : this.radius + this.offset;
     else if(this.direction == RotateDirection.RIGHT) 
      return this.radius;
     else 
      return hint ? this.radius + this.offset : this.radius;
    
  

  private resetStartAngle(angle: number) 
    this.pieItems.forEach((item) => 
      item.setSelected(false);
      item.setStartAngle(item.getStartAngle() + angle);
    );
  

  private calculateRoteAngle(item: PieItem): number 
    var result = item.getStartAngle() + item.getAngle() / 2 + this.getDirectionAngle();
    if (result >= 360) 
      result -= 360;
    
    if (result <= 180) 
      result = -result;
     else 
      result = 360 - result;
    
    return result;
  

  private calculateItemAngle() 
    var total = 0;
    this.pieItems.forEach((item) => 
      total += item.count;
    )

    for(var i = 0; i < this.pieItems.length; i++) 
      var data = this.pieItems[i];
      data.setRate(data.count / total);
      data.setAngle(data.getRate() * 360);
      if (i == 0) 
        data.setStartAngle(0);
       else 
        var preData = this.pieItems[i - 1];
        data.setStartAngle(preData.getStopAngle());
      
    
  

  private getDirectionAngle(): number 
    var result 以上是关于piechart啥意思的主要内容,如果未能解决你的问题,请参考以下文章

Android PieChart 饼图控件

Android PieChart 饼图控件

Android PieChart 饼图控件

nvd3 piechart.js - 如何编辑工具提示?

Highcharts (PieChart) - 第二次绘制时图表宽度减小

ggplot, facet, piechart:将文本放在饼图切片的中间