今天是关于一个d3画饼状图的一个简单剖析和讲解。(本次用例是基于Vue和D3 v4)
在画图之前,首先,我们来了解一下D3画图的一个重要概念,就是布局。布局是什么,其实就是数据转换。假设有数据[5,10,15,20,25,30] 。要将这个数据画成饼状图,直接使用数组中的数据是不行的,需要将数据转换成扇形的起始角度和终止角度。构成整个圆形,这个过程如果自己画,显然太过于复杂,所以d3js把这些数据转化的成功通过布局直接完成。目前已经提供来很多的布局,如Pie(饼状图),Chord(弦图),Histogram(直方图)等。
第一阶段,画一个饼图
先来一段准备好到Html 和css
<template>
<div class="demo-pie-chart">
<div class="title">我是饼状图</div>
<div id="pie-svg-wrap">
</div>
</div>
</template>
<script>
export default {
methods: {
initPie() { }
},
mounted() {
this.initPie();
}
}
</script>
<style lang="scss" scoped>
.demo-pie-chart {
color: #fff;
.title {
line-height: 60px;
font-size: 18px;
}
#pie-svg-wrap {
background: #fff;
display: inline-block;
}
}
</style>
开始准备初始数据
initPie() {
//初始化数据,这是一份2014年各大手机厂商在中国的出货量
let dataset = [
["小米", 60.8],
["三星", 58.4],
["联想", 47.3],
["苹果", 46.6],
["华为", 41.3],
["酷派", 40.1],
["其他", 111.5],
];
//创建饼状图布局和它的数据规则访问器
let pie = d3.pie().value((d)=>d[1]);
let pieData = pie(dataset);
//绘制图形的
let width = 500,
height = 500;
let outerRadius = width / 3,
innerRadius = 0;
let svg = d3.select("#pie-svg-wrap").append("svg")
.attr("width", width)
.attr("height", height)
.attr('fill', 'white');
}
在准备好基础数据之后,接下里画圆弧进行图形绘制
//创建弧生成器,弧生成器中的需要内外半径的参数
let arc = d3.arc().innerRadius(innerRadius)
.outerRadius(outerRadius);
let color = d3.schemeCategory10;
//添加对应数目的弧数,即<g>元素
let arcs = svg.selectAll("g")
.data(pieData) //绑定转换后的数据piedata
.enter()
.append("g")
.attr("transform", `translate(${width/2},${height/2})`);
//添加弧的路径元素
arcs.append("path")
.attr("fill", (d,i)=>{
return color[i];//设置每个弧的颜色
})
.attr("d", d=>{
return arc(d); //使用弧生成器获取路径信息
})
接着给每个弧添加相应的文字
这里有必要给大家讲解下 arc.centroid这个方法(以下是官方文档)
# arc.centroid(arguments…)
计算由给定 arguments 生成的 generated的中间点 [x, y]. arguments 是任意的,它们会被传递给arc
生成器的访问器。为了与生成的弧保持一致,访问器必须是确定的。例如,相同的参数返回相同的值。中间点被定义为 (startAngle + endAngle) / 2 和 (innerRadius + outerRadius) / 2。例如:
注意,中间点 并不是几何中心,因为几何中心点可能位于弧之外; 这个方法可以用来方便的对labels
进行定位。
//添加弧内的文字元素
arcs.append('text')
.attr("transform", d=>{
let x = arc.centroid(d)[0] * 1.4; //文字的x坐标
let y = arc.centroid(d)[1] * 1.4; //文字的y坐标
return `translate(${x},${y})`
})
.attr("text-anchor", "middle")
.text(d=>{
let percent = (Number(d.value) /d3.sum(dataset, d=>d[1]))*100;
return percent.toFixed(1) + "%"
})
如下图。
接下来,我们有可能会需要用到添加一些额外的信息
//添加连接弧外的文字的直线元素和文字
arcs.append("line")
.attr("stroke", "black")
.attr("x1", d=> arc.centroid(d)[0] * 2)
.attr("y1", d=> arc.centroid(d)[1] * 2)
.attr("x2", d=> arc.centroid(d)[0] * 2.2)
.attr("y2", d=> arc.centroid(d)[1] * 2.2);
arcs.append("text")
.attr("transform", d=>{
let x = arc.centroid(d)[0] * 2.5; //文字的x坐标
let y = arc.centroid(d)[1] * 2.5; //文字的y坐标
return `translate(${x},${y})`
})
.attr("fill", "black")
.attr("text-anchor", "middle")
.text(d=>d.data[0])
到这里,第一阶段就结束了。