项目基于vue-v2.x版本搭建,包含了:vue组件,组件间通信,外部js模块和vue组件的互相引用,vue-router,element-ui,axios,echarts,less(全部配置好,开箱即用)。 分区域雷达图通过d3完成,每个区域代表唯一的分类和对应的子类(每个子类坐标单位不同,也为唯一),通过多个扇形区域拼接成一个完整雷达图(regional_radar)。
分区域扇形雷达图(regional_radar),需求为将不同类别对应展示数值的子类(每个子类坐标展示唯一)都拼接到一个雷达图里显示。(用echarts没有实现坐标轴,为了方便了后面扩展所以用了d3.js)
完整项目在GitHub上面,这里面只做简单的介绍。
GitHub地址:https://github.com/dengzeyuan/regional_radar
需求如下图所示:
仔细观察坐标轴!!!每一个分类展示的坐标轴都是唯一的。
启动项目基于vue2.x,d3画图实现方法为一个大分类为一个扇形,通过遍历json数据里的类型对象个数画出对应的分类扇形,通过拼接各个扇形组成一个完整雷达图,并且实现点击不同分类区域事件(点击之后查找相应分类下所有要展示的子类,通过柱形图和折线图或者饼图等等)
一、
Json格式为如下:
第一层对象为分类,第二层对象为坐标轴展示数据
先算出平均一份坐标轴的角度,因为每个扇形边线和坐标轴也要保持一个角度所以分类也要 +1,计算方式如下:
let number = 0; //被除数
data.type.forEach(function(d,v){
number ++
d.items.forEach(function(dc,vc){
number ++;
})})
let onePiece = 2 * Math.PI/number //每份平均所占角度
1.1
先根据开始和结束角度画出第一个分类最外层扇形,如下:
dataset = {
//原属角度范围
startAngle: 0,
endAngle: onePiece * d.items.length
};
// 外层弧度
let arcPath = d3
.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
main
.append("path")
.attr("d", arcPath(dataset))
.attr("fill", "none")
.attr("class", "radiusouter")
.attr("stroke", "black");扇形的0°为箭头指向开始角度,转一圈为360(2 * Math.PI)
1.2
在画纵轴;
注意两点:
第一:因为线(line)是根据x,y轴坐标找点画线的,所以y轴向下为正,x轴向又为正,所以扇形开始从上向下,line开始从下向上,为了解
决这个问题,将计算出的line的y坐标取负值。第二:第一条线(line)和扇形边线如果不做处理就会重合,所以要把扇形开始角度多减去一个角度平均值
完成之后如下图所示:
dataset = {
//原属角度范围
startAngle: 0-onePiece , //开始角度多减去一个平均角度值!!!
endAngle: onePiece * d.items.length
};
if (k == 3) {
//和外弧保持一致
r = radius;
}
for (let i = 0; i < d.items.length; i++) {
let x = r * Math.sin(i * onePiece),
y = r * Math.cos(i * onePiece);
webs += x + "," + y + " "; //坐标练成字符串
webPoints.push({ //坐标转化为对象
x: x,
y: -y //这里取负值即可和扇形保持同向绘制!!!
});
}
根据分类下的11个子元素,所以要用11条纵轴(扇形里的红线)来展示刻度值。
1.3
添加纵轴文字展示;
文字用svg的text添加,只需要x,y坐标和上面的纵轴的x1,y1保持一致即可。
ebPoints.push({ //坐标转化为对象,用于纵轴和文字坐标
x: x,
y: -y //这里取负值即可和扇形保持同向绘制!!!
});
坐标计算结果如下:
初步得到的文字展示如下:
最后在将文字旋转就可以避免重叠,暂时先不管样式。
1.4
分别添加最内层扇形和中间扇形,分别为 内层扇形半径 = 最外层半径*0.5(填充颜色为#ddd) 和 中间扇形半径 = 最外层半径*0.7(不做填充颜色)
注意:内层扇形要在纵轴添加完之后。才能保持层级覆盖在纵轴之上。
效果如下图所示:
1.5
最后在添加刻度和分类名称,效果图如下:
刻度坐标用的是上一步中计算扇形半径时一起得出的结果。只需要通过d3的data().enter().append(“text”,function(d){...})将文字添加即可。
样式也是暂时先不调,最后一起调样式。
到此为止,一个大分类下的扇形雷达已经完成。
二、
根据之前遍历数据的过程,本次该绘制第二个区域的扇形,
从第二个扇形开始的纵轴个数和扇形角度都要累加上一次的总和。这样才能保证后面的每个纵轴(line)计算出来的x,y坐标和扇形(arc)的startAngle,endAngle都是在上一次绘制扇形结束后紧接着累加向后增长的。这样才能将多个分区的扇形拼接成360°的一整个大闭合的圆形雷达图。
if (vindex !== 0) { //之后的每个扇形都累加上一次的总和
// debugger
starts = start = ends + 1
ends = end = ends + type.items.length +1 ;
}else{ //第一个扇形
starts = start = 0;
ends = end = type.items.length;
}
纵轴和刻度坐标计算从第二个扇形开始都要累加上一次的结果
本次开始 = 上一次的结束总和 + 1(因为弧线和纵轴之间有一个平均角度的间隙所以要+1)
本次结束 = 本次数组长度 + 上一次结束总和 + 1
if(vindex == 0 ){
dataset = {
//原属角度范围
startAngle: 0-onePiece,
endAngle: onePiece * ends
};
}else{
dataset = {
//原属角度范围
startAngle: dataset.endAngle,
endAngle: onePiece * ends
};
}
角度计算方式也和坐标一样
本次角度开始 = 上一次的结束角度总和
本次角度结束 = 平均角度 * 之前所有数组长度总和
效果图如下:(字体重叠样式最后在调)
三、
根据遍历分类对象的长度依次循环画出对应个数的扇形,和对应纵轴,通过循环遍历的方法可以动态的创建分区域扇形个数和角度值大小。
四、
在计算value值在图形中的像素显示位置。
计算方式为:
let r = radius * 0.5 +(radius - radius * 0.5) *
(Number(type.items[indexflag].value) -
Number(type.items[indexflag].rangeMin)) /
(Number(type.items[indexflag].rangeMax) -
Number(type.items[indexflag].rangeMin));
Value在雷达图像素位置 = value所占显示纵轴的比例长度 + 内层圆弧半径长度
此结果不论坐标轴刻度为正和负值还是从大到小或者从小到大,雷达线所在图上的像素位置均正确。
最终完成效果图如下:
五、
可以点击不同分区进行其他操作。点击之后在相应的区域上加一层遮罩,如下所示: