记得上次看d3应该是1年前的事情了,当时还一边看一边写了d3(v5.7)的一个学习笔记:https://www.cnblogs.com/eco-just/tag/d3/

后来转战three.js就没继续研究了(其实也是感觉api层面的东西也没有深入研究的必要,何况后续项目也不会用到这些东西)。

期间也有同行通过博客问过弦图的问题,出于种种原因吧,当时并没有深入研究。

但是今天!我们就结合d3的3.5.16版本来深入解析一下d3的弦图吧。(demo是找的简书上这为同学的笔记:https://www.jianshu.com/p/4b44c708c2da

先上效果:

step1:根据数据初始化布局

code:

  1. // 初始数据
  2. var city_name = [ "北京" , "上海" , "广州" , "深圳" , "香港" ];
  3. var population = [
  4. [ , , , , ],
  5. [ , , , , ],
  6. [ , , , , ],
  7. [ , , , , ],
  8. [ , , , , ]
  9. ];
  10.  
  11. // 弦布局初始化
  12. var chord_layout = d3.layout.chord()
  13. .padding(0.03)
  14. .sortSubgroups(d3.descending)
  15. .matrix(population);
  16.  
  17. // 获取弦布局初始化后的数据
  18. var groups = chord_layout.groups();
  19. var chords = chord_layout.chords();

解析:

population数据表格化

  北京 上海 广州 深圳 香港
北京 1000 3045 4567 1234 3714
上海 3214 2000 2060 124 3234
广州 8761 6545 3000 8045 647
深圳 3211 1067 3214 4000 1006
香港 2146 1034 6745 4764 5000

先用d3.layout.chord()这个api传入数据,初始化布局所需要的数据groups、chords;

groups数据5条,5个城市,根据population所占权重分配圆弧的大小,在上述数据上的反应就是startAngle和endAngle;

chords数据15条,5个城市选两个(source,target),根据排列组合应该是5+4+3+2+1=15种(source,target可以相同);

step2:绘制画布和计算内外圆半径

  1. // svg画布
  2. var width = ;
  3. var height = ;
  4. var svg = d3.select(".d3content")
  5. .append("svg")
  6. .attr("width",width)
  7. .attr('height', height)
  8. .append("g")
  9. .attr('transform', 'translate(' + width/ + "," + height/ + ")");
  10.  
  11. var color20 = d3.scale.category20();
  12.  
  13. var innerRadius = width/ * 0.7;
  14. var outerRadius = innerRadius * 1.1;

解析:

.d3content是画布依赖的根元素dom,上述代码将会在600X600的画布上绘制接下来的弦图;

step3:绘制外圆和文字

  1. var outer_arc = d3.svg.arc()
  2. .innerRadius(innerRadius)
  3. .outerRadius(outerRadius);
  4. //绘制外圆(5个城市)
  5. var g_outer = svg.append("g");
  6. g_outer.selectAll("path")
  7. .data(groups)
  8. .enter()
  9. .append("path")
  10. .style("fill",function(d) {
  11. return color20(d.index);
  12. })
  13. .style("stroke",function(d) {
  14. color20(d.index);
  15. })
  16. .attr("d",outer_arc) // 此处调用了弧生成器
  17. ;
      //绘制文字
  18. g_outer.selectAll("text")
  19. .data(groups)
  20. .enter()
  21. .append("text")
  22. .each(function(d,i) { // 对每个绑定的数据添加两个变量
  23. d.angle = (d.startAngle + d.endAngle) / ;
  24. d.name = city_name[i];
  25. })
  26. .attr("dy",".35em")
  27. .attr('transform', function(d) { // 平移属性
  28. var result = "rotate(" + (d.angle*/Math.PI) + ")";
  29. result += "translate(0," + - * (outerRadius + ) + ")";
  30. if (d.angle > Math.PI * / && d.angle < Math.PI * / )
  31. result += "rotate(180)";
  32. return result;
  33. })
  34. .text(function(d) {
  35. return d.name;
  36. });

效果图:

注意上述有一句代码:

  1. .attr("d",outer_arc) // 此处调用了弧生成器

对于每个path都会根据这个函数来绘制,而这个函数对于对应源码里的d3.svg.arc,并且这里有个隐藏的东西:

.attr("d",out_arc),第二个参数执行的时候(他是一个函数),会将数据作为实参传给他,于是到了源码里:

  1. d3.svg.arc = function() {
  2. var innerRadius = d3_svg_arcInnerRadius,
  3.     outerRadius = d3_svg_arcOuterRadius,
  4.      cornerRadius = d3_zero,
  5.      padRadius = d3_svg_arcAuto,
  6.      startAngle = d3_svg_arcStartAngle,
  7.      endAngle = d3_svg_arcEndAngle,
  8.      padAngle = d3_svg_arcPadAngle;
  9. function arc() {
  10. var r0 = Math.max(, +innerRadius.apply(this, arguments)),
  11.       r1 = Math.max(, +outerRadius.apply(this, arguments)),
  12.       a0 = startAngle.apply(this, arguments) - halfπ,
  13.       a1 = endAngle.apply(this, arguments) - halfπ,
  14.       da = Math.abs(a1 - a0),
          cw = a0 > a1 ? : ;
  15. if (r1 < r0) rc = r1, r1 = r0, r0 = rc;
  16. if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, - cw) : "") + "Z";
  17. var rc, cr, rp, ap, p0 = , p1 = , x0, y0, x1, y1, x2, y2, x3, y3, path = [];
  18.   .....
  19. }

这个arguments就是如下这样的单条数据:

最终根据传入的数据,加上一系列的逻辑处理返回了一个path节点的d属性值,具体的判断逻辑,我截图一张供各位欣赏,如果你有兴趣可以逐个去找他的函数:

最后,由这个属性值绘制了一个path节点:

后面的绘制文字就不多说了,注意一点,回调函数形参里面的d是data(数据)的意思,i是index(索引)的意思。

step4:绘制内弦chord

  1. // 弦生成器
  2. var inner_chord = d3.svg.chord()
  3. .radius(innerRadius);
  4. console.log(inner_chord)
  5.  
  6. // 添加g元素,接下来在这个元素里面绘制chord
  7. var g_inner = svg.append("g")
  8. .attr("class","chord");
  9.  
  10. g_inner.selectAll("path")
  11. .data(chords)
  12. .enter()
  13. .append("path")
  14. .attr("d",inner_chord) // 调用弦的路径值
  15. .style("fill",function(d,i) {
  16. return color20(d.source.index);
  17. })
  18. .style("opacity",)
  19. ;

为了大家看得清晰,我把之前绘制的外圆注释了:

解析:

同理,这里通过d3.svg.chord来绘制弦,依据的数据是:

同样贴上源码的主要部分:

  1. d3.svg.chord = function() {
  2. var source = d3_source,
  3.      target = d3_target,
  4.      radius = d3_svg_chordRadius,
  5.      startAngle = d3_svg_arcStartAngle,
  6.      endAngle = d3_svg_arcEndAngle;
  7. function chord(d, i) {
  8. var s = subgroup(this, source, d, i),
  9.       t = subgroup(this, target, d, i);
  10. return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z";
  11. }

equals(s,t)判断两个端点是否相同来决定绘制的方式;

我们看到这里绘制路径,主要用到了两个函数arc()和curve();

  1.    function arc(r, p, a) {
  2. return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p;
  3. }
  4. function curve(r0, p0, r1, p1) {
  5. return "Q 0,0 " + p1;
  6. }

关于svg的path绘制中各参数的含义,下面给一张图,这里就不多说了:

所以弦主要就是由svg内置的弧绘制api来绘制的(普通的弧线/贝塞尔曲线)!

深入解析d3弦图的更多相关文章

  1. D3.js系列——布局:弦图和集群图/树状图

    一.弦图 1.弦图是什么 弦图(Chord),主要用于表示两个节点之间的联系的图表.两点之间的连线,表示谁和谁具有联系. 2.数据 初始数据为: var city_name = [ "北京& ...

  2. 【 D3.js 入门系列 --- 9.3 】 弦图生产

    我个人的博客: www.ourd3js.com csdn博客为: blog.csdn.net/lzhlzz 转载请注明出处,谢谢. 弦图( Chord ),主要用于表示两个节点之间的联系.例如以下图: ...

  3. D3.js 弦图的制作

    这是一种用于描述节点之间联系的图表. 1. 弦图是什么 弦图(Chord),主要用于表示两个节点之间的联系. 两点之间的连线,表示谁和谁具有联系: 线的粗细表示权重: 2. 数据 初始数据为: var ...

  4. ZOJ 1015 Fishing Net(弦图判定)

    In a highly modernized fishing village, inhabitants there make a living on fishery. Their major tool ...

  5. 【BZOJ1006】【HNOI2008】神奇的国度(弦图染色)

    1006: [HNOI2008]神奇的国度 Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 1467  Solved: 603[Submit][Stat ...

  6. 弦图的判定MCS算法(zoj1015)

    题意:裸的弦图的判定: 弦图定义:给出一个无向连通图,如果每个环中都存在至少一条弦(环中存在不相邻的两点直接相连)这样的图叫做弦图: 转载:http://blog.csdn.net/crux_d/ar ...

  7. ZOJ 1015 Fishing Net(判断弦图)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=15 题意:给定一个图.判断是不是弦图? 思路:(1)神马是弦图?对于一 ...

  8. BZOJ 1006 神奇的国度(弦图的染色数)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1006 题意:给定一个弦图,求最小染色数.就是用最小数目的颜色进行染色使得任意两个相邻的节 ...

  9. bzoj 1242: Zju1015 Fishing Net 弦图判定

    1242: Zju1015 Fishing Net弦图判定 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 214  Solved: 81[Submit ...

随机推荐

  1. C#信号量(Semaphore,SemaphoreSlim)

    Object->MarshalByRefObject->WaitHandle->Semaphore 1.作用: 多线程环境下,可以控制线程的并发数量来限制对资源的访问 2.举例: S ...

  2. MyBatis-08-使用注解开发

    8.使用注解开发 8.1.面向接口编程 面向接口编程的根本原因:解耦,可拓展,提高复用,分层开发中.上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性好 8.2.使用注解开发 注解在 ...

  3. Zabbix Web 中文字体显示问题

  4. Java中的集合Collection、Iterator和Foreach用法(一)

    1.Java集合概述 在编程中,常常需要集中存放多个数据.当然我们可以使用数组来保存多个对象.但数组长度不可变化,一旦在初始化时指定了数组长度,则这个数组长度是不可变的,如果需要保存个数变化的数据,数 ...

  5. Codeforces Round #586 (Div. 1 + Div. 2) C. Substring Game in the Lesson

    链接: https://codeforces.com/contest/1220/problem/C 题意: Mike and Ann are sitting in the classroom. The ...

  6. nginx之location部署yii项目(不使用nginx端口转发)

    前言: 之前部署yii项目的时候, 使用的是域名, 后来使用nginx进行端口转发(反向代理)来部署yii项目. 这一次部署尝试只使用location 进行部署(不需要使用端口). 先贴出nginx的 ...

  7. 【Python之路】特别篇--服务商API认证、Restful、一致性哈希

    API加密方式 1/ 加密方式: Md5 (随机字符串 + 时间戳) 2/ 发送方式: http://127.0.0.1:8888/index?pid= MD5加密值 | 时间戳 | 序号 服务端接收 ...

  8. HDOJ4467 ( 分块 思想 )

    题目:链接:http://acm.hdu.edu.cn/showproblem.php?pid=4467 题意:给你n个点(每个点都有一个颜色,0代表黑色,1代表白色),m条边,每条边有一个权值.现在 ...

  9. Linux进程通信之文件

    父子进程共享打开的文件描述符------使用文件完成进程间通信. /*** fork_share_fd.c ***/ #include <stdio.h> #include <uni ...

  10. codeforces723E

    One-Way Reform CodeForces - 723E There are n cities and m two-way roads in Berland, each road connec ...