使用D3 V3版本绘制
绘制效果图:
准备工作: 3d立体柱状图的绘制
绘制地图思路:
- 绘制地图及地图投影;
- 根据地图地理信息将柱状图放置到指定位置;
- 根据scale计算出柱状图实际高度。
1. 绘制地图
地图的绘制可参考 十二月咖啡馆:十五课 地图,下面是地图绘制代码。
其中mapJson为GeoJSON文件,可在DATAV.GeoAtlas下载。
// 基础数据初始化
const svgObj = {
width: 800,
height: 750,
};
const mapColor = '#114a93';
const mapLineColor = '#0d67c7';
// 绘制
const svg = d3.select(node).append('svg')
.attr({ width: svgObj.width, height: svgObj.height });
// 定义地图的投影
const projection = d3.geo.mercator()
.center([119.621184, 28.511708])
.scale(19000);
// 定义地理路径生成器
const path = d3.geo.path()
.projection(projection);
const mapGroups = svg.append('g')
.attr('class', 'mapGroups');
// 地图路径绘制
mapGroups.selectAll('path')
.data(mapJson.features)
.enter()
.append('path')
.attr('class', 'map-path')
.style('fill', () => mapColor)
.attr('stroke', mapLineColor)
.attr('d', path);
// text地理名称
mapGroups.selectAll('text')
.data(mapJson.features)
.enter()
.append('text')
.attr('x', (d) => projection(d.properties.centroid)[0])
.attr('y', (d) => projection(d.properties.centroid)[1])
.attr('transform', `translate(${0},${0})`)
.text((d) => d.properties.name)
.attr({
'font-size': '16px',
});
地图底部的阴影部分可依据相同办法绘制出路径,填充不同的线条及背景颜色,即可完成基础地图的绘制。
2. 添加地区数据
设置模拟数据如下:
dataset: [
{ id: 1, name: '遂昌县', rise: 3, unit: '%' },
{ id: 2, name: '龙泉市', rise: 2, unit: '%' },
{ id: 3, name: '松阳县', rise: 4, unit: '%' },
{ id: 4, name: '云和县', rise: 2, unit: '%' },
{ id: 5, name: '庆元县', rise: 3, unit: '%' },
{ id: 6, name: '青田县', rise: 4, unit: '%' },
{ id: 7, name: '莲都区', rise: 2, unit: '%' },
{ id: 8, name: '景宁县', rise: 6, unit: '%' },
{ id: 9, name: '缙云县', rise: 2, unit: '%' },
],
相关代码:
const rectWidth = 13;
const rectHeight = 60; // 应根据计算得出,先默认柱体高度为60
const rectColor = '#14a8f394';
const rectLineColor = '#12ddda';
const rect3dGroups = svg.append('g')
.attr('class', 'rect3dGroups');
const rect3d = rect3dGroups.selectAll('rect3d')
.data(mapJson.features)
.enter()
.append('g')
.attr('class', 'rect3d')
.style('transform', (d, i) => {
if (i === 7) { // 景宁县全称过长,单独设置样式
return `translate(${-30 - 65}px, ${-4 + 25}px)`;
}
return 'translate(-30px, -4px)';
});
// 绘制顶面
rect3d.append('path')
.attr('d', (d) => `M${projection(d.properties.centroid)[0]} ${projection(d.properties.centroid)[1] - rectHeight}l${rectWidth} ${-rectWidth}l${-rectWidth} ${-rectWidth}l${-rectWidth} ${rectWidth}z`)
.attr({
fill: rectColor,
stroke: rectLineColor,
'stroke-width': 1,
'shape-rendering': 'crispedges',
});
// 绘制侧面
rect3d.append('path')
.attr('d', (d) => `M${projection(d.properties.centroid)[0]} ${projection(d.properties.centroid)[1]}l0 ${-rectHeight}l${rectWidth} ${-rectWidth}l0 ${rectHeight}l${-rectWidth} ${rectWidth}l${-rectWidth} ${-rectWidth}l0 ${-rectHeight}l${rectWidth} ${rectWidth}z`)
.attr({
fill: rectColor,
stroke: rectLineColor,
'stroke-width': 1,
'shape-rendering': 'crispedges',
});
可参考 W3School SVG 线性渐变 将柱体颜色设置为渐变色,此处未做渐变处理。
效果图:
3. 根据数据计算柱体高度
// 定义柱体高度比例尺
const maxData = d3.max(dataset.map((item) => item.rise)); // 最大增长率对应高度为60
const rectHScale = d3.scale.linear() // 设置线性比例尺
.domain([0, maxData])
.range([0, 60]);
则侧面与顶面 path 路径 d 中的 rectHeight 应根据数据计算高度,代码如下:
.attr('d', (d)=>{
let rectHeight = 0;
for (let i = 0; i < dataset.length; i += 1) {
if (d.properties.name.indexOf(dataset[i].name.slice(0, -1)) !== -1) {
rectHeight = rectHScale(dataset[i].rise);
}
}
return ...
})
结果如图:
最后可根据设计图在柱状体旁边添加相关数据,也可添加相关动态效果。