本节笔者带领读者实现一个饼状图 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