背景:d3 画堆积条形图,每一个条形 显示多种类型所占的百分比
数据:
效果:
数据的 column 就是图上每个 x ..
每个条形就是显示的每列的值(竖着看)..
网上也有demo,但是我们的数据和人家的不太一样,我们是从列来看,然后数据已经是做好百分比的处理了..
重点:
columns 就是 样本 columns[0] 是 events
我是用 for 循环画了12 次条形,然后每个条形去堆叠每个 events 的百分比
拿第一个条形来说,
第一个蓝色 rect 它的 x 是第一个 sample,y 值是 yAxis(它的值),
height 值是 yAxis(0) - y (它的值)
从第二个开始,
色块的 y 值 就是 yAxis(当前色块的值+之前所有色块的值)
height 值 就是 yAxis(之前所有的色块值)- y(当前色块的值 + 之前所有色块的值)
代码如下:
let margin = {
left: 50,
right: 30,
top: 30,
bottom: 80
}
let width = 900
let height = 600
let svg = d3.select('#canvas')
.append('svg')
.attr('id', 'svg')
.attr('width', 1000)
.attr('height', height)
let tooltip = d3.select('body')
.append('div')
.style('position', 'absolute')
.style('z-index', '10')
.style('visibility', 'hidden')
.style('font-size', '14px')
.style('font-weight', 'bold')
.text('')
var x = d3.scaleBand()
.domain(this.samplesArr)
.rangeRound([margin.left, width - margin.right])
.padding(0.3)
var y = d3.scaleLinear()
.domain([0, 100])
.rangeRound([height - margin.bottom, margin.top])
var color = d3.scaleOrdinal(d3.schemeCategory20)
var stack = d3.stack()
.offset(d3.stackOffsetExpand);
svg.append("g")
.attr("transform", "translate(0," + y(0) + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.style("text-anchor", "start")
.attr("transform", "rotate(45 -10 10)");
svg.append("g")
.attr("transform", "translate(" + margin.left + ",0)")
.call(d3.axisLeft(y).tickFormat(d => d + "%" ))
for (let i = 0;i < this.samplesArr.length;i++) {
let sum = 0
let sum2 = 0
let index = i
let sample = this.samplesArr[i]
svg.selectAll(".rect")
.data(this.data)
.enter().append("rect")
.attr("x", (d) => {
return x(sample);
})
.attr("y", (d, i) => {
sum = sum + d[index]
return y(sum)
})
.attr("height", (d, i) => {
sum2 = sum2 + d[index]
if (i === 0) {
return y(0) - y(d[index])
} else {
return y(sum2-d[index]) - y(sum2)
}
})
.attr("width", x.bandwidth())
.attr("fill", (d,i) => {
return color(i)
})
.on('mouseover', (d, i) => {
return tooltip.style('visibility', 'visible').text(this.eventsArr[i] + ' ' + d[index] + '%')
})
.on('mousemove', function (d, i) {
return tooltip.style('top', (event.pageY-10)+'px').style('left',(event.pageX+10)+'px')
})
.on('mouseout', function (d, i) {
return tooltip.style('visibility', 'hidden')
})
}
复制代码
2.这里再说一下图例,因为画出来的和图上色块的颜色顺序相反,所以把图例的数组 reverse 了一下~
let legend = svg.append("g")
.attr("transform", "translate(" + (width - 30) + ","+ margin.top +")")
legend.selectAll("rect")
.data(this.eventsArrReverse) // 为了和图的颜色从上到下一样
.enter().append("rect")
.attr("x",(d,i)=>{
return 0
})
.attr("y",(d,i)=>{
return 15 * i + 5 * i // 每个 rect 间隔为5
})
.attr("width",15)
.attr("height",15)
.attr("fill",(d,i) => {
return color(this.eventsArr.length - 1 - i)
})
legend.selectAll("text")
.data(this.eventsArrReverse)
.enter().append("text")
.attr("x",(d,i)=>{
return 20
})
.attr("y",(d,i)=>{
return 15 * i + 5 * i + 12
})
.text((d) => {
return d
})
复制代码
ps:提一下 数组的 reverse,会把原数组也修改了..
可以用下面方法做到 reverse 而不改变原数组
this.eventsArrReverse = Array.from(this.eventsArr).reverse()
复制代码