前面写了一篇 【d3.js入门】 基本饼图,这次我们在这个面积图基础上添加一些动画效果。
我们继续绘制D3.js饼图
绘制饼图
首先,定义饼图的数据集,包括每个区块的名称和数值:
let dataset = [
{ name: "春", value: 20 },
{ name: "夏", value: 50 },
{ name: "秋", value: 20 },
{ name: "冬", value: 80 }
];
然后,设置饼图的颜色集合,其中 scaleOrdinal
方法用于生成一个颜色比例尺,输入参数是一个数组,每个元素对应一个颜色:
let color = d3.scaleOrdinal([
"#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3",
"#a6d854", "#ffd92f", "#e5c494", "#b3b3b3",
"#8dd3c7", "#ffffb3", "#bebada", "#fb8072",
"#80b1d3", "#fdb462", "#b3de69", "#fccde5",
"#d9d9d9", "#bc80bd", "#ccebc5", "#ffed6f",
"#1f78b4", "#33a02c", "#cab2d6", "#6a3d9a"
]);
接着,定义SVG容器的尺寸,并设置饼图半径:
let svgWidth = 600;
let svgHeight = 400;
let radius = Math.min(svgWidth, svgHeight) / 3;
接下来,创建一个弧生成器。d3.arc()
用于创建圆弧生成器, .innerRadius()
和 .outerRadius()
方法分别用于设置内半径和外半径。
let arcGenerator = d3.arc()
.innerRadius(radius * 0.5)
.outerRadius(radius);
然后,创建一个布局,也就是将数据转化为可绘制饼图的数据格式,使用 d3.pie()
方法即可。注意 value
函数返回每个部分所占比例。
let pieLayout = d3.pie()
.sort(null)
.value(function (d) {
return d.value;
});
接下来,生成最终的饼图数据,使用 .join()
方法将新数据绑定到选择集上,返回更新后的选择集。这里使用 pieLayout()
方法将原始数据转换为可用于 d3.arc()
的格式。
let pieData = pieLayout(dataset);
let svg = d3.select(".chart")
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
.style('border', '1px solid #999999');
let pieContainer = svg.append("g")
.attr("transform", "translate(" + svgWidth / 2 + "," + svgHeight / 2 + ")");
let piePaths = pieContainer.selectAll(".arc")
.data(pieData)
.join("path")
.attr("class", "arc")
.attr("d", arcGenerator)
.attr("fill", function (d, i) {
return color(i);
})
.each(function (d) {
this._current = d;
});
到目前为止,基本的饼图已经绘制完成了,然后我开始添加动画。
在之前的基础上,我们现在向代码中添加了一些交互元素,如更新数据、正向排序、反向排序、添加数据、删除数据等功能。这些按钮可以帮助用户进行数据与图表的交互操作。
首先,使用 innerHTML
设置按钮文字:
let innerHtml = ['更新数据', '正向排序', '反向排序', '添加数据', '删除数据'];
然后,创建一个空数组 buts
和一个 <div>
元素,在其中遍历 innerHtml
数组,生成相应的按钮,并将它们添加到 <div>
元素中和 buts
数组中:
let buts = [];
let butdiv = document.createElement('div');
dom.appendChild(butdiv);
innerHtml.map(item => {
let but = document.createElement('button');
but.innerHTML = item;
butdiv.appendChild(but);
buts.push(but);
})
接下来,通过点击不同的按钮来实现饼图数据的更新和排序,以及数据的添加和删除等操作。其中,单击“更新数据”按钮时,重新生成随机数据,然后重新绘制和过渡饼图数据,并更新图例:
buts[0].onclick = function () {
// 生成新的随机数据
dataset.forEach(item => {
item.value = Math.random() * 90 | 0 + 10;
});
pieLayout.sort(null);
// 生成新的饼图数据
pieData = pieLayout(dataset);
// 计算过渡状态的函数
let arcTween = function (d, i) {
let interpolate = d3.interpolate(this._current, pieData[i]);
this._current = interpolate(0);
return function (t) {
return arcGenerator(interpolate(t));
};
};
// 更新饼图数据
piePaths = pieContainer.selectAll(".arc")
.data(pieData)
.join("path")
.attr("class", "arc")
.attr("d", arcGenerator)
.attr("fill", function (d, i) {
return color(i);
});
// 添加过渡动画
piePaths.transition()
.duration(1000)
.attrTween("d", arcTween);
// 更新图例 texts
addLegend();
};
其他功能的实现与此类似,不再一一解释。
在线演示和源码地址:scqilin.github.io/d3js/basic-…