记得上次看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:

// 初始数据
var city_name = [ "北京" , "上海" , "广州" , "深圳" , "香港" ];
var population = [
[ , , , , ],
[ , , , , ],
[ , , , , ],
[ , , , , ],
[ , , , , ]
]; // 弦布局初始化
var chord_layout = d3.layout.chord()
.padding(0.03)
.sortSubgroups(d3.descending)
.matrix(population); // 获取弦布局初始化后的数据
var groups = chord_layout.groups();
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:绘制画布和计算内外圆半径

// svg画布
var width = ;
var height = ;
var svg = d3.select(".d3content")
.append("svg")
.attr("width",width)
.attr('height', height)
.append("g")
.attr('transform', 'translate(' + width/ + "," + height/ + ")"); var color20 = d3.scale.category20(); var innerRadius = width/ * 0.7;
var outerRadius = innerRadius * 1.1;

解析:

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

step3:绘制外圆和文字

    var outer_arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
//绘制外圆(5个城市)
var g_outer = svg.append("g");
g_outer.selectAll("path")
.data(groups)
.enter()
.append("path")
.style("fill",function(d) {
return color20(d.index);
})
.style("stroke",function(d) {
color20(d.index);
})
.attr("d",outer_arc) // 此处调用了弧生成器
;
  //绘制文字
g_outer.selectAll("text")
.data(groups)
.enter()
.append("text")
.each(function(d,i) { // 对每个绑定的数据添加两个变量
d.angle = (d.startAngle + d.endAngle) / ;
d.name = city_name[i];
})
.attr("dy",".35em")
.attr('transform', function(d) { // 平移属性
var result = "rotate(" + (d.angle*/Math.PI) + ")";
result += "translate(0," + - * (outerRadius + ) + ")";
if (d.angle > Math.PI * / && d.angle < Math.PI * / )
result += "rotate(180)";
return result;
})
.text(function(d) {
return d.name;
});

效果图:

注意上述有一句代码:

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

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

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

d3.svg.arc = function() {
var innerRadius = d3_svg_arcInnerRadius,
    outerRadius = d3_svg_arcOuterRadius,
     cornerRadius = d3_zero,
     padRadius = d3_svg_arcAuto,
     startAngle = d3_svg_arcStartAngle,
     endAngle = d3_svg_arcEndAngle,
     padAngle = d3_svg_arcPadAngle;
function arc() {
var r0 = Math.max(, +innerRadius.apply(this, arguments)),
      r1 = Math.max(, +outerRadius.apply(this, arguments)),
      a0 = startAngle.apply(this, arguments) - halfπ,
      a1 = endAngle.apply(this, arguments) - halfπ,
      da = Math.abs(a1 - a0),
      cw = a0 > a1 ? : ;
if (r1 < r0) rc = r1, r1 = r0, r0 = rc;
if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, - cw) : "") + "Z";
var rc, cr, rp, ap, p0 = , p1 = , x0, y0, x1, y1, x2, y2, x3, y3, path = [];
  .....
}

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

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

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

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

step4:绘制内弦chord

    // 弦生成器
var inner_chord = d3.svg.chord()
.radius(innerRadius);
console.log(inner_chord) // 添加g元素,接下来在这个元素里面绘制chord
var g_inner = svg.append("g")
.attr("class","chord"); g_inner.selectAll("path")
.data(chords)
.enter()
.append("path")
.attr("d",inner_chord) // 调用弦的路径值
.style("fill",function(d,i) {
return color20(d.source.index);
})
.style("opacity",)
;

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

解析:

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

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

d3.svg.chord = function() {
var source = d3_source,
     target = d3_target,
     radius = d3_svg_chordRadius,
     startAngle = d3_svg_arcStartAngle,
     endAngle = d3_svg_arcEndAngle;
function chord(d, i) {
var s = subgroup(this, source, d, i),
      t = subgroup(this, target, d, i);
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";
}

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

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

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

关于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. js过滤时间格式

    Date.prototype.Format = function(fmt) { //author: meizz var o = { "M+" : this.getMonth()+1 ...

  2. centos6、7系统初始化脚本

    #!/bin/bash # #******************************************************************** #encoding -*-utf ...

  3. HDU - 5033 Building (单调栈+倍增)

    题意:有一排建筑,每座建筑有一定的高度,宽度可以忽略,求在某点的平地上能看到天空的最大角度. 网上的做法基本都是离线的...其实这道题是可以在线做的. 对于向右能看到的最大角度,从右往左倍增维护每个时 ...

  4. bzoj3993: [SDOI2015]星际战争(二分+最大流)

    题目描述 3333年,在银河系的某星球上,X军团和Y军团正在激烈地作战. 在战斗的某一阶段,Y军团一共派遣了N个巨型机器人进攻X军团的阵地,其中第i个巨型机器人的装甲值为Ai.当一个巨型机器人的装甲值 ...

  5. qt install (1)

    直接在命令行安装 sudo apt-get install qt5-default qtcreator 命令行安装的卸载 sudo apt-get remove qt5-default qtcreat ...

  6. IVIEW组件的render方法在Table组件中的使用

    后端项目地址:https://gitee.com/wlovet/table-server 前端项目地址:  https://gitee.com/wlovet/table-project 一.Rende ...

  7. 云栖社区 Tensorflow快餐教程

    云栖社区 Tensorflow快餐教程(1) - 30行代码搞定手写识别:https://yq.aliyun.com/articles/582122云栖社区 Tensorflow快餐教程(2) - 标 ...

  8. Django常见命令

    在Django的使用过程中需要使用命令让Django进行一些操作,例如创建Django项目,启动Django程序,创建新的APP,数据库迁移等. 1. 创建Django项目 新建一个文件夹来存放项目文 ...

  9. Codeforces 1111 E. Tree(虚树,DP)

    题意 有一棵树,q个询问,每次询问,指定一个点做树根,再给定k个点,要求把这些点分成不超过m组的方案数,分配的限制是任意两个有祖先关系的点不能分在同一组.题目还保证了所有的询问的k加起来不超过1e5. ...

  10. JSON数据的缓存

    前端有时候会遇到JSON数据的缓存,后台给我们JSON数据是一个对象,直接缓存起来它存的是字符串 "[object Object]".这是因为在缓存时会隐式调用toString方法 ...