本文将视图了解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深入理解的更多相关文章

  1. 对numpy中shape的理解

    from:http://blog.csdn.net/by_study/article/details/67633593 环境:Windows, Python3.5 一维情况: >>> ...

  2. d3js layout 深入理解

    D3 layouts help you create more advanced visualisations such as treemaps: D3 layouts帮助您创造更加高级复杂的可视化图 ...

  3. d3js scales深入理解

    转自:https://www.cnblogs.com/kidsitcn/p/7182274.html 比例尺函数是这样的javascript函数: 接收通常是数字,日期,类别等data输入并且: 返回 ...

  4. d3js selections深入理解

    D3 selections选择DOM元素以便可以对这些dom元素做相应的操作,比如:更改其style,修改其属性,执行data-join操作,或者插入.删除相应elements 比如,如果给定5个ci ...

  5. numpy中 array数组的shape属性

    numpy.array 的shape属性理解 在码最邻近算法(K-Nearest Neighbor)的过程中,发现示例使用了numpy的array数组管理,其中关于array数组的shape(状态)属 ...

  6. 对Tensorflow中tensor的理解

    Tensor即张量,在tensorflow中所有的数据都通过张量流来传输,在看代码的时候,对张量的概念很不解,很容易和矩阵弄混,今天晚上查了点资料,并深入了解了一下,简单总结一下什么是张量的阶,以及张 ...

  7. Python中numpy的应用

    #创建ndarray import numpy as np nd = np.array([2,4,6,'])#numpy中默认ndarray的所有元素的数据类型是相同,如果数据的类型不同,会统一为统一 ...

  8. tensor数据基操----索引与切片

    玩过深度学习图像处理的都知道,对于一张分辨率超大的图片,我们往往不会采取直接压平读入的方式喂入神经网络,而是将它切成一小块一小块的去读,这样的好处就是可以加快读取速度并且减少内存的占用.就拿医学图像处 ...

  9. 机器学习实战基础(二十四):sklearn中的降维算法PCA和SVD(五) PCA与SVD 之 重要接口inverse_transform

    重要接口inverse_transform  在上周的特征工程课中,我们学到了神奇的接口inverse_transform,可以将我们归一化,标准化,甚至做过哑变量的特征矩阵还原回原始数据中的特征矩阵 ...

随机推荐

  1. java.lang.Exception: The server rejected the connection: None of the protocols were accepted

    solution for this is from comment for https://issues.jenkins-ci.org/browse/JENKINS-29616. The follow ...

  2. Mac服务管理-Launchd(转)

    背景: 在Mac下没有像Linux那样有很多的关于init方面的工具,从init的发展历史https://en.wikipedia.org/wiki/Init上可以知道,Mac使用的是Launchd作 ...

  3. VS2015编译Boost1.64

    一.下载并解压:boost1.64.0:http://www.boost.org/users/history/version_1_64_0.html 二.以管理员权限运行VS2015命令行工具 三.c ...

  4. 13.Reflect

    1.概述 Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API.Reflect对象的设计目的有这样几个. (1) 将Object对象的一些明显属于语言内部的方法(比如O ...

  5. TortoiseGit学习系列之Windows上TortoiseGit的安装详解(图文)

    不多说,直接上干货! TortoiseGit的安装准备 首先你得安装windows下的msysgit. 安装版本控制器客户端TortoiseGit [不习惯英文的朋友,也可以下个语言包]. 下载地址: ...

  6. Capture Conversion解读

    Let G name a generic type declaration with n type parameters A1,...,An with corresponding bounds U1, ...

  7. Java 中 String 的常用方法(一)

    上一篇介绍了 String 中的几个常用构造方法,由 String 这个核心对象发散出去关于字符的编码,字符的字节表达,对 GC 的影响,正则表达式,模式匹配,这可能是 Java 里内涵最丰富的对象了 ...

  8. 【转】如何在ASP.NET 2.0中定制Expression Builders

    expressions是asp.net 2.0中的新特色,它可以使你在asp.net的页面里很方便的使用自定义的属性. 在ASPX页里只要使用$符号就可以访问到,你定制的属性了. 例如我们看个例子: ...

  9. JS数组和函数 小记

    数组 JS中的数组来自window,是一个全局的对象,typeof的值是'object'. 创建数组: 1.Array(3):当只传一个值的时候,会生成一个长度为该数值的空数组. 2.Array(3, ...

  10. java项目部署运行

      运用插件运行项目: tomcat-maven-plugin:1.1   1.右键项目-->run as -->5 maven build-->tomcat:run         ...