0
点赞
收藏
分享

微信扫一扫

通过svg/canvas path绘制饼图(代码为c#,逻辑平台无关)

path参数

  1. M x,y 将起点移动到指定位置
  2. L x,y 从起点绘制直线到目标位置
  3. A 20,20 0 1 0 0,1 起点 起点x,起点y 画椭圆 长轴,短轴 旋转角度 是否是优弧 正角方向绘制 终点x,终点y
  4. z 绘制闭合路径
    TIP:对应的参数均有对应的小写表示,如m,l,a
    使用小写字母的参数坐标对应起点位置的相对坐标,
    大小字母表示绝对坐标

绘图思路

  1. 每一次移动起点到圆心
  2. 绘制对应的扇形的从圆心到圆上的某条边(长度为圆的半径)
  3. 绘制对应的圆弧
  4. 闭合路径

需要解决的问题

  1. 计算对应的圆弧的终点(扇形的半径为圆的半径,起点为直线的终点)
  2. 将终点对应绘图的实际坐标(空间转换)

解决思路

  1. 使用三角函数根据弧度(根据半分比通过2PI计算)、半径(已知)计算对应的x、y长度
  2. 空间转换根据实际绘图空间坐标转换
    (例左上角为0,0,水平向右和水平向下对应x,y轴正方向,此时转换需要将x加上一半的宽度(将x移动到圆心),将y轴翻转再加上一半高度)

绘图逻辑代码

namespace Aixueshi.ExamManagement.UserControls.CommonControl
{
public class PieChartViewModel
{
/// <summary>
/// 绘制颜色
/// </summary>
//public Brush Brushes { get; set; }

/// <summary>
/// 百分比,范围[0-1]
/// </summary>
public double Percent { get; set; }

/// <summary>
/// 颜色
/// </summary>
public string Color { get; set; }
}
/// <summary>
/// 饼图
/// 设置上下文为List<PieChartViewModel>
/// 可通过设置所有percent之和小于1来绘制纯扇形
/// !!! 注意:需同时设置PieChart组件的容器Width、Height,否则会导致对应的宽度计算为NULL !!!
/// </summary>
public partial class PieChart : UserControl
{
double ChartWidth { get { return this.Width; } }
double ChartHeight { get { return this.Height; } }
double MidWidth
{
get
{
return ChartWidth / 2;
}
}
double MidHeight
{
get
{
return ChartHeight / 2;
}
}

//记录上一次绘图结束位置x
double lastX;
//记录上一次绘图结束位置y
double lastY;

double lastPercent = 0;

//List<PieChartViewModel> model { get; set; }
public PieChart()
{
InitializeComponent();

//DataContext = model;
}

private void Clear()
{
canvas.Children.Clear();
lastX = ChartWidth;
lastY = MidHeight;
lastPercent = 0;
}

private StringBuilder MoveMid(StringBuilder str)
{
return str.Append($"M {MidWidth} {MidHeight} ");
}
private StringBuilder AddLine(StringBuilder str)
{
return str.Append($"L {lastX} {lastY} ");
}


private void AddArc(StringBuilder str, double percent)
{


double x = MidWidth, y = MidHeight;
if (lastPercent >= 0 && lastPercent < 1)
{
//绘制整圆
if (lastPercent == 0 && lastPercent + percent >= 1)
{
str.Append($"A {MidWidth},{MidHeight} 0 0 0 {0}, {y} z ");
MoveMid(str);
str.Append($"L {0} {y} ");
str.Append($"A {MidWidth},{MidHeight} 0 0 0 {ChartWidth}, {y} z ");
return;
}
lastPercent += percent;
if (lastPercent > 1)
{
lastPercent = 1;
}
var arc = lastPercent * (2 * Math.PI);
var reletiveX = Math.Round(Math.Cos(arc) * MidWidth);
var reletiveY = -Math.Round(Math.Sin(arc) * MidWidth);

x += reletiveX;
y += reletiveY;
str.Append($"A {MidWidth},{MidHeight} 0 0 0 {x}, {y} z ");

//更新坐标
lastX = x;
lastY = y;
}
else
{
if (lastPercent < 0)
{

}
else
{

}
}
}

public void Add(double percent, string color)
{
var data = new StringBuilder();

MoveMid(data);
AddLine(data);
AddArc(data, percent);

var path = new Path();
path.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString(color));
path.Data = Geometry.Parse(data.ToString());
canvas.Children.Add(path);
}
int index = 0;
SolidColorBrush randomBrush()
{
if (index > 3)
{
index = 0;
}
index++;
switch (index)
{
case 1:
return new SolidColorBrush((Color)ColorConverter.ConvertFromString("#34E1B6"));
case 2:
return new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FB7468"));
case 3:
return Brushes.Gray;
default:
break;
}
return Brushes.Black;
}

private void pieChart_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
Clear();
if (this.DataContext is List<PieChartViewModel> data)
{
foreach (var item in data)
{
Add(item.Percent, item.Color);
}
}
}
}
}



举报

相关推荐

0 条评论