d3js shape深入理解
本文将视图了解d3js提供的帮助我们创建矢量图形的helper函数,比如下面的:
http://d3indepth.com/shapes/
lines
curves
pie chart segments
symbols
SVG
首先我们来认识一下SVG(scalable vector graphics).要知道上面例子中的图形实际上都是由SVG的path元素构成的。每张图都有不同的path元素来组成,这些path元素本身的d属性来定义图形的path.而path data由一系列的命令组成(比如: M0,80L100,100L200,30L300,50L400,40L500,80),如下面的代码所示:
<path d="M12.061098497445768,-54.465861201009005A4,4,0,0,1,17.122890866582928,-57.50483986910224A60,
60,0,0,1,29.32181418613459,-52.34721781368898A4,4,0,0,1,30.66836903336433,-46.59883339919623L5.38995775477549,
-9.649482084709554A1.2313393364975447,1.2313393364975447,0,0,1,3.1668950166423597,-10.589377935221732Z"></path>
这些微代码由浏览器来解读,比如 ‘move to’ , ‘draw a line to’等,详细可以参考 SVG specification
实际上,我们可以自己通过书写代码来创建这些path命令集,但是你会发现,写出这些代码虽然并不难,但是一定是很繁琐的,d3js为了将我们从这些繁琐的工作中解放出来,d3js的作者就发明了被成为"path generator"的路径生成器。下面我们列出常见的路径生成器:
line | Generates path data for a multi-segment line (typically for line charts) |
area | Generates path data for an area (typically for stacked line charts and streamgraphs) |
stack | Generates stack data from multi-series data:实际上stack是一个layout |
arc | Generates path data for an arc (typically for pie charts) |
pie | Generates pie angle data from array of data:实际上pie是一个layout |
symbol | Generates path data for symbols such as plus, star, diamond |
Line generator
我们给d3一个(x,y)坐标的数组,d3就能生成一串path data string
我们首先声明一个line generator:
var lineGenerator = d3.line();
lineGenerator的任务就是接收一个坐标数组,输出一个path data string直接可以作为path元素的d属性值。
我们来定义一个坐标数组
var points = [
[0, 80],
[100, 100],
[200, 30],
[300, 50],
[400, 40],
[500, 80]
];
接着我们传入points参数来调用lineGenerator,
var pathData = lineGenerator(points);
// pathData is "M0,80L100,100L200,30L300,50L400,40L500,80"
lineGenerator完成的工作就是创建了一个M(move to)和L(line to)命令的字符串,这样我们就可以使用pathData来给d属性赋值
d3.select('path')
.attr('d', pathData);
最终浏览器渲染出来下面的线图:
对于line generator函数,我们可以有以下可以配置
.x()
and.y()
accessor functions,.defined()
(to handle missing data),.curve
(to specify how the points are interpolated) and.context()
to render to a canvas element.
.x() and .y() accessor functions
By default each array element represents a co-ordinate defined by a 2-dimensional array (e.g. [0, 100]
). However we can specify how the line generator interprets each array
默认情况下每一个数组元素都代表了一个2纬的数组,比如[0,100],然而我们也可以告诉line generator来如何解读传入的数据,而这就要使用对应的accessor functions了:.x()和.y().例如假设我们有以下对象的数组:
var data = [
{value: 10},
{value: 50},
{value: 30},
{value: 40},
{value: 20},
{value: 70},
{value: 50}
];
我们就可以这样来定义accessor函数:
lineGenerator
.x(function(d, i) {
return xScale(i);
})
.y(function(d) {
return yScale(d.value);
});
在这个例子中,我们使用数组的index来定义x,(注意我们同时使用了比例尺函数)
.defined()
我们可以使用defined函数来定义如果有部分数据不应该渲染的情况下如何操作,比如下面的数据:
var points = [
[0, 80],
[100, 100],
null,
[300, 50],
[400, 40],
[500, 80]
];
我们告知line generator每一个坐标只有是non-null时才是有效的,通过下面的代码:
lineGenerator
.defined(function(d) {
return d !== null;
});
这样当我们再次调用lineGenerator并渲染后就将得到一个有着断续的line:
注意:如果没有上面的.defined定义的话,将会产生一个错误,因为null无法获取到对应的x,y坐标
.curve()
我们也可以定义这个path的points之间是如何插值的。比如,我们可以使用一个B-spline算来来插值:
var lineGenerator = d3.line()
.curve(d3.curveCardinal);
虽然有相当多的不同类型的curve type在d3中可以使用,但是我们也可以把这些插值类型简单分为两类:一种是必须经过指定points的类型(比如:curveLinear,curveCardinal,curveCatmullRom,curveMonotone,curveNatural,curveStep),另一种是不必经过每一个points(比如curveBasics和curveBundle)
See the curve explorer for more information.
Rendering to canvas
By default the shape generators output SVG path data. However they can be configured to draw to a canvas element using the .context()
function:
默认情况下shape generator输出SVG path data.然而我们也可以通过.context()函数来指定使用canvas来绘图:
var context = d3.select('canvas').node().getContext('2d'); lineGenerator.context(context); context.strokeStyle = '#999';
context.beginPath();
lineGenerator(points);
context.stroke();
Radial line
The radial line generator is similar to the line generator but the points are transformed by angle (working clockwise from 12 o’clock) and radius, rather than x
and y
:
radial line generator和普通的line generator是类似的,唯一的不同是该generator对坐标的解读是“极坐标系”(从12点开始顺时针运行的)角度和半径,而不是x,y坐标:
var radialLineGenerator = d3.radialLine(); var points = [
[0, 80],
[Math.PI * 0.25, 80],
[Math.PI * 0.5, 30],
[Math.PI * 0.75, 80],
[Math.PI, 80],
[Math.PI * 1.25, 80],
[Math.PI * 1.5, 80],
[Math.PI * 1.75, 80],
[Math.PI * 2, 80]
]; var pathData = radialLineGenerator(points);
Accessor functions .angle()
and .radius()
are also available:
类似于lineGenerator,对应的accessor function .angle()和.radius()如下:
radialLineGenerator
.angle(function(d) {
return d.a;
})
.radius(function(d) {
return d.r;
}); var points = [
{a: 0, r: 80},
{a: Math.PI * 0.25, r: 80},
{a: Math.PI * 0.5, r: 30},
{a: Math.PI * 0.75, r: 80},
...
]; var pathData = radialLineGenerator(points);
Area generator
The area generator outputs path data that defines an area between two lines. By default it generates the area between y=0
and a multi-segment line defined by an array of points:
区域生成器输出一个通过两条lines来定义的一个区域的path data来工作的。默认情况下,它在y=0的水平线和一个由一个点数组来定义的多段线线之间生成一个区域:
var areaGenerator = d3.area(); var points = [
[0, 80],
[100, 100],
[200, 30],
[300, 50],
[400, 40],
[500, 80]
]; var pathData = areaGenerator(points);
我们可以通过.y0() accessor函数来定义这个base line:
areaGenerator.y0(150);
就变成了如下的图形:(通常情况下我们使用图形的height作为base line)
我们也可以给.y0() accessor函数一个函数来指明如何获取y0的值,类似于.y1() accessor
areaGenerator
.x(function(d) {
return d.x;
})
.y0(function(d) {
return yScale(d.low);
})
.y1(function(d) {
return yScale(d.high);
}); var points = [
{x: 0, low: 30, high: 80},
{x: 100, low: 80, high: 100},
{x: 200, low: 20, high: 30},
{x: 300, low: 20, high: 50},
{x: 400, low: 10, high: 40},
{x: 500, low: 50, high: 80}
];
典型地,.y0()定义了base line, .y1()定义了top line. 注意我们也使用了.x()定义了x坐标(base line和top line使用的是相同的x坐标哦!!)
和line generator一样,我们可以指定在点之间是如何插值的(.curve()),以及如何处理missing data(.defined())以及如何在canvas而不是在svg中渲染(.context())
Radial area
radial area generator和area generator是类似的,唯一的不同是这些点我们是使用angle角度(从12点顺时针开始的角度)和半径radius来定义的,而不是x和y来定义的:
var radialAreaGenerator = d3.radialArea()
.angle(function(d) {
return d.angle;
})
.innerRadius(function(d) {
return d.r0;
})
.outerRadius(function(d) {
return d.r1;
}); var points = [
{angle: 0, r0: 30, r1: 80},
{angle: Math.PI * 0.25, r0: 30, r1: 70},
{angle: Math.PI * 0.5, r0: 30, r1: 80},
{angle: Math.PI * 0.75, r0: 30, r1: 70},
{angle: Math.PI, r0: 30, r1: 80},
{angle: Math.PI * 1.25, r0: 30, r1: 70},
{angle: Math.PI * 1.5, r0: 30, r1: 80},
{angle: Math.PI * 1.75, r0: 30, r1: 70},
{angle: Math.PI * 2, r0: 30, r1: 80}
];
如下图所示:
Stack generator
stack generator接收一个multi-series data而对每一个series来生成一个数组,而每个数组包含着各data point的lower和upper values
lower and upper values are computed so that each series is stacked on top of the previous series.
lower和upper values被用于计算位置这样每个series都堆叠在前一个series上面
var data = [
{day: 'Mon', apricots: 120, blueberries: 180, cherries: 100},
{day: 'Tue', apricots: 60, blueberries: 185, cherries: 105},
{day: 'Wed', apricots: 100, blueberries: 215, cherries: 110},
{day: 'Thu', apricots: 80, blueberries: 230, cherries: 105},
{day: 'Fri', apricots: 120, blueberries: 240, cherries: 105}
]; var stack = d3.stack()
.keys(['apricots', 'blueberries', 'cherries']); var stackedSeries = stack(data); // stackedSeries = [
// [ [0, 120], [0, 60], [0, 100], [0, 80], [0, 120] ], // Apricots
// [ [120, 300], [60, 245], [100, 315], [80, 310], [120, 360] ], // Blueberries
// [ [300, 400], [245, 350], [315, 425], [310, 415], [360, 465] ] // Cherries
// ]
.keys()配置函数定义了在stack generation中哪些series被包含其中。
stack generator输出的数据,你可以随意使用,但是典型地,这个输出数据被用于产生stacked bar charts:
or when used in conjunction with the area generator, stacked line charts:
或者,如果和area generator配合使用,形成stacked line charts:
.order()
stacked series出现的顺序可以由.order()配置函数来
stack.order(d3.stackOrderInsideOut);
每个series汇总后通过选择的顺序来排列,可用的顺序如下:
stackOrderNone | (Default) Series in same order as specified in .keys() |
stackOrderAscending | Smallest series at the bottom |
stackOrderDescending | Largest series at the bottom |
stackOrderInsideOut | Largest series in the middle |
stackOrderReverse | Reverse of stackOrderNone |
.offset()
默认情况下stacked series的baseline为0.然而我们也可以配置stack generator的offset来达到不同的baseline效果。比如,我们可以规整stacked series以便他们都有着相同的高度:
stack.offset(d3.stackOffsetExpand);
可用的offset如下:
stackOffsetNone | (Default) No offset |
stackOffsetExpand | Sum of series is normalised (to a value of 1) |
stackOffsetSilhouette | Center of stacks is at y=0 |
stackOffsetWiggle | Wiggle of layers is minimised (typically used for streamgraphs) |
下面就是一个使用了stackOffsetWiggle的stacked chart
:
Arc generator
Arc generators produce path data from angle and radius values. An arc generator is created using:
arc generator用于从angle和radius值来产生path data.下面创建一个arc
var arcGenerator = d3.arc();
随后可以给该generator传入一个包含着startAngle, endAngle,innerRadius,outerRadius属性值的对象来生成path data:
var pathData = arcGenerator({
startAngle: 0,
endAngle: 0.25 * Math.PI,
innerRadius: 50,
outerRadius: 100
}); // pathData is "M6.123233995736766e-15,-100A100,100,0,0,1,70.71067811865476,-70.710678
// 11865474L35.35533905932738,-35.35533905932737A50,50,0,0,0,3.061616997868383e-15,-50Z"
(注意:startAngle
and endAngle
是从12点开始顺时针计量的角度数)
Configuration
We can configure innerRadius
, outerRadius
, startAngle
, endAngle
so that we don’t have to pass them in each time:
我们可以使用innerRadius,outerRadius,startAngle,endAngle函数来配置,而不用每次生成arc generator时传入对象参数:
arcGenerator
.innerRadius(20)
.outerRadius(100); pathData = arcGenerator({
startAngle: 0,
endAngle: 0.25 * Math.PI
}); // pathData is "M6.123233995736766e-15,-100A100,100,0,0,1,70.71067811865476,-70.71067811
// 865474L14.142135623730951,-14.14213562373095A20,20,0,0,0,1.2246467991473533e-15,-20Z"
We can also configure corner radius (cornerRadius
) and the padding between arc segments (padAngle
and padRadius
):
我们也可以配置corner radius(corner Radius)以及弧线段之间的padding值(padAngle,padRadius)
arcGenerator
.padAngle(.02)
.padRadius(100)
.cornerRadius(4);
Arc padding takes two parameters padAngle
and padRadius
which when multiplied together define the distance between adjacent segments. Thus in the example above, the padding distance is 0.02 * 100 = 2
. Note that the padding is calculated to maintain (where possible) parallel segment boundaries.
Arc padding有两个参数:padAngle和padRadius他们合在一起定义了两个相邻弧段之间的距离。这样在上面的例子中,padding distance就是0.02*100=2.注意padding计算时会尽可能地保持(如果可能的话)弧段之间的边界保持平行。
你可能会问
你可能会问为什么不用一个简单的padDistance参数来定一个这个padding distance,而使用两个参数相乘这么复杂的参数来定义呢?之所以这样,是因为pie generator无需关心半径的大小。
Accessor functions
我们也可以为startAngle, endAngle, innerRadius,outerRadius来定义对应的accessor functions
arcGenerator
.startAngle(function(d) {
return d.startAngleOfMyArc;
})
.endAngle(function(d) {
return d.endAngleOfMyArc;
}); arcGenerator({
startAngleOfMyArc: 0,
endAngleOfMyArc: 0.25 * Math.PI
});
Centroid
有时,计算弧线的中心点是很有用的,比如当我们要放置label时,往往希望放到中心点附件,d3给我们提供了一个.centroid()函数来实现:
arcGenerator.centroid({
startAngle: 0,
endAngle: 0.25 * Math.PI
});
// returns [22.96100594190539, -55.43277195067721]
下面的例子我们使用.centroid()来计算label的位置:
// Create an arc generator with configuration
var arcGenerator = d3.arc()
.innerRadius(20)
.outerRadius(100); var arcData = [
{label: 'A', startAngle: 0, endAngle: 0.2},
{label: 'B', startAngle: 0.2, endAngle: 0.6},
{label: 'C', startAngle: 0.6, endAngle: 1.4},
{label: 'D', startAngle: 1.4, endAngle: 3},
{label: 'E', startAngle: 3, endAngle: 2* Math.PI}
]; // Create a path element and set its d attribute
d3.select('g')
.selectAll('path')
.data(arcData)
.enter()
.append('path')
.attr('d', arcGenerator); // Add labels, using .centroid() to position
d3.select('g')
.selectAll('text')
.data(arcData)
.enter()
.append('text')
.each(function(d) {
var centroid = arcGenerator.centroid(d);
d3.select(this)
.attr('x', centroid[0])
.attr('y', centroid[1])
.attr('dy', '0.33em')
.text(d.label);
});
Pie generator
The pie generator goes hand in hand with the arc generator. Given an array of data, the pie generator will output an array of objects containing the original data augmented by start and end angles:
pie generator和弧线generator是类似的。给pie generator一个数据数组,pie generator就会输出一个反映了原始start和end angles数据的对象数
var pieGenerator = d3.pie();
var data = [10, 40, 30, 20, 60, 80];
var arcData = pieGenerator(data); // arcData is an array of objects: [
// {
// data: 10,
// endAngle: 6.28...,
// index: 5,
// padAngle: 0,
// startAngle: 6.02...,
// value: 10
// },
// ...
// ]
We can then use an arc generator to create the path strings:
随后,我们可以使用一个arc generator来创建对应的path strings
var arcGenerator = d3.arc()
.innerRadius(20)
.outerRadius(100); d3.select('g')
.selectAll('path')
.data(arcData)
.enter()
.append('path')
.attr('d', arcGenerator);
注意pieGenerator的输出包含了startAngle和endAngle两个属性。这些正好是arcGenerator所需要的输入!
再注意:实际上pieGenerator是一个layout了,而不是路径生成器!
The pie generator has a number of configuration functions including .padAngle()
, .startAngle()
, .endAngle()
and .sort()
. .padAngle()
specifies an angular padding (in radians) between neighbouring segments.
pie generator包含了一系列的配置函数,包括:.padAngle(),.startAngle(),.endAngle(),.sort(),.padAngle()定义了一个在相邻段之间的angular padding
.startAngle()
and .endAngle()
配置pie chart的startAngle和endAngle. 这将允许创建半圆形的pie charts:
var pieGenerator = d3.pie()
.startAngle(-0.5 * Math.PI)
.endAngle(0.5 * Math.PI);
默认情况下段的起始和结束角度使得段之间以降序排列。然而我们可以更改这种排序方式,方法是使用.sort()函数:
var pieGenerator = d3.pie()
.value(function(d) {return d.quantity;})
.sort(function(a, b) {
return a.name.localeCompare(b.name);
}); var fruits = [
{name: 'Apples', quantity: 20},
{name: 'Bananas', quantity: 40},
{name: 'Cherries', quantity: 50},
{name: 'Damsons', quantity: 10},
{name: 'Elderberries', quantity: 30},
];
Symbols
The symbol generator produces path data for symbols commonly used in data visualisation:
symbol generator用于产生在可视化领域中常用的symbol的path data:
var symbolGenerator = d3.symbol()
.type(d3.symbolStar)
.size(80); var pathData = symbolGenerator();
随后我们就可以使用这个pathData变量作为path元素的d属性了:
d3.select('path')
.attr('d', pathData);
var symbolGenerator = d3.symbol()
.type(d3.symbolStar)
.size(80); var points = [
[0, 80],
[100, 100],
[200, 30],
[300, 50],
[400, 40],
[500, 80]
]; var pathData = symbolGenerator(); d3.select('g')
.selectAll('path')
.data(points)
.enter()
.append('path')
.attr('transform', function(d) {
return 'translate(' + d + ')';
})
.attr('d', pathData);
D3包含了下面的symbol types:
var symbolGenerator = d3.symbol()
.size(100); var symbolTypes = ['symbolCircle', 'symbolCross', 'symbolDiamond', 'symbolSquare', 'symbolStar', 'symbolTriangle', 'symbolWye']; var xScale = d3.scaleLinear().domain([0, symbolTypes.length - 1]).range([0, 700]); d3.select('g')
.selectAll('path')
.data(symbolTypes)
.enter()
.append('path')
.attr('transform', function(d, i) {
return 'translate(' + xScale(i) + ', 0)';
})
.attr('d', function(d) {
symbolGenerator
.type(d3[d]); return symbolGenerator();
}); d3.select('g')
.selectAll('text')
.data(symbolTypes)
.enter()
.append('text')
.attr('transform', function(d, i) {
return 'translate(' + xScale(i) + ', 40)';
})
.text(function(d) {
return 'd3.' + d;
});
d3js shape深入理解的更多相关文章
- 对numpy中shape的理解
from:http://blog.csdn.net/by_study/article/details/67633593 环境:Windows, Python3.5 一维情况: >>> ...
- d3js layout 深入理解
D3 layouts help you create more advanced visualisations such as treemaps: D3 layouts帮助您创造更加高级复杂的可视化图 ...
- d3js scales深入理解
转自:https://www.cnblogs.com/kidsitcn/p/7182274.html 比例尺函数是这样的javascript函数: 接收通常是数字,日期,类别等data输入并且: 返回 ...
- d3js selections深入理解
D3 selections选择DOM元素以便可以对这些dom元素做相应的操作,比如:更改其style,修改其属性,执行data-join操作,或者插入.删除相应elements 比如,如果给定5个ci ...
- numpy中 array数组的shape属性
numpy.array 的shape属性理解 在码最邻近算法(K-Nearest Neighbor)的过程中,发现示例使用了numpy的array数组管理,其中关于array数组的shape(状态)属 ...
- 对Tensorflow中tensor的理解
Tensor即张量,在tensorflow中所有的数据都通过张量流来传输,在看代码的时候,对张量的概念很不解,很容易和矩阵弄混,今天晚上查了点资料,并深入了解了一下,简单总结一下什么是张量的阶,以及张 ...
- Python中numpy的应用
#创建ndarray import numpy as np nd = np.array([2,4,6,'])#numpy中默认ndarray的所有元素的数据类型是相同,如果数据的类型不同,会统一为统一 ...
- tensor数据基操----索引与切片
玩过深度学习图像处理的都知道,对于一张分辨率超大的图片,我们往往不会采取直接压平读入的方式喂入神经网络,而是将它切成一小块一小块的去读,这样的好处就是可以加快读取速度并且减少内存的占用.就拿医学图像处 ...
- 机器学习实战基础(二十四):sklearn中的降维算法PCA和SVD(五) PCA与SVD 之 重要接口inverse_transform
重要接口inverse_transform 在上周的特征工程课中,我们学到了神奇的接口inverse_transform,可以将我们归一化,标准化,甚至做过哑变量的特征矩阵还原回原始数据中的特征矩阵 ...
随机推荐
- 高性能web服务器(热死你)Resin Linux的安装、配置、部署,性能远超Nginx支持Java、PHP等
高性能web服务器(热死你)Resin Linux的安装.配置.部署,性能远超Nginx支持Java.PHP等 一. 安装resin 1. 下载resin: 下载地址:http://cauch ...
- 搭建类似生产环境的RAC
install the necessary packages yum install -y 创建组.用户和目录 创建用户组 groupadd -g 2000 oinstallgroupadd -g 2 ...
- redux在componentDidMount中出现的问题 --- state 不变
遇到这样一个问题: 在组件的componentDidMount中,我需要使用到redux中存储的某个状态. 但是有趣的是,当我再render中使用相同的状态时,状态会改变,但是在conponentDi ...
- Git学习系列之Git基本操作提交项目(图文详解)
前面博客 Git学习系列之Git基本操作克隆项目(图文详解) 然后可以 cd 切换到 LispGentleIntro 目录, 新增或者修改某些文件.这里只是模拟一下操作, 实际情况可能是 使用 Ecl ...
- InnoDB的分区表
分区功能并不是在存储引擎层完成的,因此不只有InnoDB存储引擎支持分区,常见的存储引擎MyISAM.NDB等都支持.但也并不是所有的存储引擎都支持,如CSV.FEDERATED.MERGE等就不支持 ...
- CyclicBarrier正确的使用方法和错误的使用方法
CyclicBarrier是java推出的一个并发编程工具,它用在多个线程之间协同工作.线程约定到达某个点,到达这个点之后的线程都停下来,直到最后一个线程也到达了这个点之后,所有的线程才会得到释放.常 ...
- 弗格森&红魔
本周日曼联对阵斯旺西时,一位名叫皮特-莫利纽克斯的58岁老人,将会举起一张老旧的床单,就如他曾做过的那样.那一次“臭名昭着”的行为要追溯到1989年12月,弗格森当时已经来到了悬崖边上,他的背后是一堵 ...
- 如何处理服务器SSL收到了一个弱临时Diffie-Hellman 密钥?
当我们用火狐浏览器打开某个HTTPS网站时可能会失败,并且出现如下错误提示: 安全连接失败连接某个URL网址时发生错误. 在服务器密钥交换握手信息中 SSL 收到了一个弱临时 Diff ...
- 查询MySQL数据表的字段名和表结构
查询表的字段: -- 查询表的字段名 SELECT COLUMN_NAME -- GROUP_CONCAT('a.', COLUMN_NAME SEPARATOR ',') AS COLUMN_NAM ...
- mysql导入外部.sql文件时错误
当前环境: 操作系统:windows 7 mysql版本:5.5.36 MySQL Community Server (GPL) 当我第一次导入.sql文件时报错: mysql> source ...