前言
本文章适合有适当d3基础的同学阅读,全程阅读大约30分钟。
阅读前可以了解下:
- [SVG元素] (Docute)
- [D3比例尺] (人类身份验证 - SegmentFault)
- [贝塞尔曲线] (深度掌握SVG路径path的贝塞尔曲线指令 " 张鑫旭-鑫空间-鑫生活)
- [SVG线性渐变] (SVG 线性渐变)
- [选择器、数据绑定、元素操作] (D3 v5 学习笔记-1 选择器)
实现效果
因其实现效果如孔雀开屏一般,所以称其为孔雀图
场景描述
小明是一家大型公司的老总,公司旗下有32家子公司。小明想要知道各子公司的成立时间以及公司规模情况,还想分析总公司以及各子公司去年一年的利润额和营业额的占比情况,除此之外还想知道有哪些子公司有业务异常情况。
数据准备
- 方便起见,以下所有的业务主体我们都称为节点,总公司我们称为根节点,子公司我们称为子节点。
- 以下数据均为mock数据,只为展示所用,并无实际考究,如有不当,请忽略。
{
画图
- 初始化画布
- 首先我们得有个容器:
<
加点装饰:
//
2. 在容器中初始化一个 800 x 660 的画布:
const
- 定义全局入场动画
我们期望图表的入场是有一个动画效果的,为了保持统一,我们在全局定义一个动画效果。需要动画的时候只需要.transition(t())就可以。
const
- 单圆方法
从实现效果图可以看出,圆是整个图表的最主要的元素,所以我们这里封装一个单圆方法。
- setStyleMap方法:遍历样式对象,给ele元素赋予attr属性值:
/**
2. drawCircle方法:画单个圆,包含6个参数,参数含义参考如下代码注释:
/**
- 双圆方法
有了单圆方法,我们就可以基于单圆封装一个双圆方法。包含9个参数,参数含义参考如下代码注释:
/**
- 画根节点
此时我们可以开始画图了,先画根节点,起到一个初始定位的作用。
- 常量定义及数据计算。
// 给纵轴留出的左侧距离,以下很多定位需要用到这个常量
2. 调用drawDoubleCircle方法,画根节点。
// h - 1 是为了让根节点底部没有被截掉的感觉
3. 画根节点标签值。
rootNodeG
根节点效果如图:
- 画子节点
首先思考一下,有了双圆方法,我们要画子节点还缺少什么。
- 半径最大最小值的限制,不然无法计算半径比例尺;
- 异常子节点波纹动画的扩散距离是需要考虑的,涉及横轴和纵轴两个比例尺的计算;
- 纵轴0刻度与根节点的距离,涉及纵轴比例尺的计算;
- 三个比例尺的定义,包括横轴比例尺、纵轴比例尺、子节点半径比例尺;
- 子节点渐变颜色的设置;
- 鼠标hover高亮效果的实现。
我们一个一个解决:
1. 常量定义。
// 子节点半径最大值
2. 根据数据定义三个比例尺。
// 横轴:时间比例尺
3. 定义线性渐变过滤器。
我们这里使用<linearGradient>来定义一个线性渐变,渐变定义在<defs>引用容器中,然后给渐变定义一个id号,在需要赋予颜色的元素上调用此id号即可。
// 颜色值计算
4. 画子节点。
// 定义子节点总容器
效果图如下,异常节点为灰色,其余为渐变色:
5. 添加异常情况下波纹动画。
/**
遍历画子节点的代码中添加`drawRippleAnimate`方法:
...
效果图如下,异常节点添加了波纹动画:
- 画连线路径
- 定义贝塞尔曲线方法。
/**
2. 画连接路径。
// 定义连线路径容器
此时你会发现一个问题,视觉上连接路径浮在子节点上,所以我们将 连接路径提到 子节点之前画。
- 画网格线、刻度、及纵轴标题
1. 常量定义及刻度值计算。
// 定义刻度数量
2. 画网格线及刻度值。
// 定义坐标轴线容器
3. 画纵轴标题。
axisG
同样的道理,这一步再提到 连接路径之前画,防止出现浮在上面的视觉问题。
此时dom结构如下:
实现效果图如下:
- 添加hover子节点时的高亮效果
/**
遍历画子节点的代码中添加添加drawHoverStyle方法:
...
.each(function (d, index) { // 遍历去画子节点
...
drawHoverStyle(nodeG)
})
尝试一下hover子节点,效果如下:
结语
至此我们就逐步实现了这个图表,此图表的优势在于:展示多维度的数据并且不失美观,感兴趣同学可以画起来哦,如有不明确或不合理的地方,欢迎留言指正。
https://codepen.io/sienhang/pen/wLgNeKcodepen.io