堆栈图布局可以计算二维数组每一数据层的基线???
看图说话,堆栈图可以用于三维数据的展示,x轴和y轴 是两个维度,新增了颜色维度,也就是三个维度,堆栈图的好处是可以将数据堆叠起来,关于堆栈图的衍生,用区域生产器绘制堆栈图暂且放一放,本文主要展示上图实现的代码。
<template>
<div id='svgContainer' style="">
<div class="every">
<h3>堆栈图</h3>
<div class="svg" id="stack"></div>
</div>
</template>
<script>
import * as d3 from 'd3'
export default {
methods: {
stack () {
let width = 400
let height = 400
let padding = {
left: 40,
right: 10,
top: 20,
bottom: 20
}
let margin = 10 // 控制堆栈图的间隙
let data = [
{month: '2015-1-1', apples: 3840, bananas: 1920, cherries: 960, oranges: 300},
{month: '2015-1-2', apples: 1600, bananas: 1440, cherries: 960, oranges: 400},
{month: '2015-1-3', apples: 640, bananas: 960, cherries: 640, oranges: 200},
{month: '2015-1-4', apples: 320, bananas: 480, cherries: 640, oranges: 500}
]
// 数据转换器
let stack = d3.stack()
.keys(['apples', 'bananas', 'cherries', 'oranges'])
.order(d3.stackOrderNone)// 使用原始数据的顺序不进行顺序调整
.offset(d3.stackOffsetNone)
let stackData = stack(data)
let colorZ = d3.scaleOrdinal(d3.schemeCategory10) // 这里color其实是第三维度,他代表水果种类
// x比例尺
let xScale = d3.scaleBand()
.range([0, width - padding.left - padding.right])
// x值域,其实就是月份
xScale.domain(data.map((d) => d.month))
let yScale = d3.scaleLinear()
.range([height - padding.top - padding.bottom, 0])
// y值域,求的是转化后的数组的最后一个数组中的第二个元素的最大值,绕的我都有点晕,最大值怎么求可以自己写function
yScale.domain([0, d3.max(stackData[stackData.length - 1], (item) => item[1])])
// x轴和y轴
let xAxis = d3.axisBottom().scale(xScale)
let yAxis = d3.axisLeft(yScale)
// 绘图
let svg = d3.select('#stack')
.append('svg')
.attr('width', width)
.attr('height', height)
// 添加x轴
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(' + padding.left + ',' + (height - padding.bottom) + ')')
.call(xAxis)
// 添加y轴
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(' + padding.left + ',' + padding.top + ')')
.call(yAxis)
// 将二维数组的第一维剥离,打散成n列
let rectContainer = svg.selectAll('rectContainer')
.data(stackData)
.enter()
.append('g')
.attr('class', 'rectContainer')
.attr('fill', (d, i) => { return colorZ(d.key) })
// 渲染每一列
rectContainer.selectAll('rect')
.data((d) => d)
.enter()
.append('rect')
.attr('x', (d) => { return xScale(d.data.month) + padding.left + margin / 2 })
.attr('y', (d) => { return yScale(d[1]) + padding.top })
.attr('width', (d) => { return xScale.bandwidth() - margin })
.attr('height', (d) => { return height - padding.top - padding.bottom - yScale(d[1] - d[0]) })
.attr('stroke', '#ccc')
// 添加描述
svg.selectAll('circle')
.data(['apples', 'bananas', 'cherries', 'oranges'])
.enter()
.append('circle')
.attr('cx', (d) => { return width - padding.right - 80 })
.attr('cy', (d, i) => { return padding.top + 25 * i })
.attr('r', '6')
.attr('fill', (d) => { return colorZ(d) })
let texts = svg.selectAll('textContainer')
.data(['apples', 'bananas', 'cherries', 'oranges'])
.enter()
.append('g')
.attr('class', 'textContainer')
texts.append('text')
.attr('x', (d) => { return width - padding.right - 60 })
.attr('y', (d, i) => { return padding.top + 25 * i })
.attr('dy', '0.32em')
.text((d) => d)
.attr('fill', (d) => colorZ(d))
}
},
mounted () {
this.stack()
}
}
</script>
<style lang="less">
#svgContainer{
width: 100%;
height: 100%;
.every{
width: 400px;
height: 425px;
margin:15px;
float: left;
h3{
margin:0;
.button{
float: right;
margin-right: 20px;
font-size: 14px;
cursor: pointer;
padding: 2px 8px;
border:1px solid #ccc;
background: yellowgreen;
border-radius: 4px;
&:hover{
background: violet;
}
}
}
.svg{
width: 400px;
height: 400px;
}
}
}
</style>