背景
需求要实现一个类似于汉诺塔的结构,但是还有一点不一样,每一层的大小是打乱的,不是按照顺序排列的,塔的两侧是两个同类别但是不同值的属性值,这个图的作用主要用来对比两个“实体”的所有属性的值。
设计
在实际设计的时候,发现每个“实体”50个属性值,其中属性值可以分为两类:
- 定性类:比如派系,类别等属性,这一类属性统称为定性类,一般在可视化设计的时候,用颜色映射。
- 定量类:比如流量,大小,方向等数值类的属性统称为定量类,一般在可视化设计的时候,用长短,或者大小来映射。
有了这些设计,那么方案也就出来了,就是设计一个汉诺塔结构的坐标轴,一直竖直方向,一个横向等坐标轴,竖直方向的轴放在中间,如下图所示。然后就是两侧采用不用方向的横向条形图来可视化,数值类属性的条形图全部采用刚蓝色显示,将具体的数值使用颜色来映射。定性类属性采用颜色映射,所以条形图在表示定性类属性的时候,长度全部是最大值。
核心代码
1. 绘制坐标轴
添加svg
let self = this;
self.infoSvg = d3
.select("#attr-compare-div")
.append("svg")
.attr("class", "infosvg")
.attr("width", "400px")
.attr("height", "790px");
设置x轴缩放,绘制x轴
let xScale = d3
.scaleBand()
.domain(["当前节点", "对比节点"])
.range([20, self.InfoSvgWidth]);
let xAxis = self.infoSvg
.append("g")
.attr("class", "xAixs")
.attr("transform", "translate(0, " + self.InfoSvgHeight + ")")
.call(d3.axisBottom(xScale));
设置y轴缩放,绘制y轴
let yNumNameList = [];
for (let index = 1; index <= 30; index++) {
yNumNameList.push("num" + index);
}
for (let index = 1; index <= 20; index++) {
yNumNameList.push("culster" + index);
}
console.log(yNumNameList);
self.yAxisScale = d3
.scaleBand()
.domain(yNumNameList)
.range([0, self.InfoSvgHeight]);
let yAxis = self.infoSvg
.append("g")
.attr("class", "yAixs")
.attr("transform", "translate(" + self.InfoSvgWidth * 0.5 + ",0)")
.call(d3.axisLeft(self.yAxisScale));
2. 绘制横向条形图
首先添加svg
let barChartSvgLeft = d3
.select(".infosvg")
.append("g")
.attr("class", "barChartSvgLeft");
处理数据,将50个属性放在list中
let currAllData = [];
currAllData.push(...node.attr_num_list);
currAllData.push(...node.attr_culster_list);
绘制横向条形图
let bar = barChartSvgLeft
.selectAll(".bar_left")
.data(currAllData)
.enter()
.append("rect");
bar
.attr("x", function(d, i) {
if (i <= 29) {
return 0.5 * self.InfoSvgWidth - self.infoSvgXScaleList[i](d.value);
} else {
return 0;
}
})
.attr("y", function(d, i) {
return i * self.yScaleBandWidth;
})
.attr("width", function(d, i) {
if (i <= 29) return self.infoSvgXScaleList[i](d.value);
else return self.InfoSvgWidth / 2;
})
.attr("height", function(d, i) {
return self.yScaleBandWidth - 2;
})
.attr("fill", function(d, i) {
if (i <= 29) return "steelblue";
else {
// console.log(self.colorScale(d.value));
return self.colorScale(d.value);
}
});
需要重新绘制Y轴,这样就不会被条形图遮挡
if (!d3.selectAll(".yAixs").empty()) {
d3.selectAll(".yAixs").remove();
}
//需要重新绘制Y轴,这样就不会被条形图遮挡
let yAxis = self.infoSvg
.append("g")
.attr("class", "yAixs")
.attr("transform", "translate(" + self.InfoSvgWidth * 0.5 + ",0)")
.call(d3.axisLeft(self.yAxisScale));
设置条形图文字
barChartSvgLeft
.selectAll(".bar-text")
.data(currAllData)
.enter()
.append("text")
.text(function(d) {
return d.value;
})
.attr("x", function(d, i) {
// console.log("x is "+0.5*self.InfoSvgWidth-self.infoSvgXScaleList[i](d.value));
if (i <= 29)
return 0.5 * self.InfoSvgWidth - self.infoSvgXScaleList[i](d.value);
else return 0;
})
.attr("y", function(d, i) {
// console.log("y is "+(i*self.yScaleBandWidth-self.yScaleBandWidth/2));
return i * self.yScaleBandWidth + self.yScaleBandWidth / 2 + 4;
})
.attr("fill", "white");