d3知识
d3文档介绍
d3各版本变化
d3小知识点或技巧
gx.call(xAxis)与xAxis(gx)相等
1 | //生成底部轴线 |
d3的图形与布局
柱状图、折线图、散点图 是d3常见的图形。
除以上三种图形外,还有很多图形他们位置不固定(还有其他说法),比如饼图,饼图可以在任意位置,这个时候,就需要一种其他的画图方式,这种方式就是d3的布局。
d3的布局用法为 d3.layout.pie,d3的布局有12种,饼图是其中最简单一个。
d3的坐标轴方向
transition 动画
概述
transition用来定义动画,点击看完整demo:1
2
3
4
5
6
7
8
9
10
11
12circle1.transition()
.duration(3000)
.attr("cx",200)
.delay(2000)
.transition()
.duration(3000)
.attr("cx",400)
.each("start",function () { d3.select(this).attr("fill","blue");} )
.transition()
.duration(3000)
.attr("cy",400)
.each("start",function () { d3.select(this).attr("fill","red");} )
duration
duration定义延迟
each
each可以定义start、end等行为
生成坐标轴
概述和代码
坐标轴一般用比例尺来做,用到比例尺就要用到两个概念 domain 与 range。
domain为实际数据范围,range为坐标轴的刻度范围,这个刻度范围是用来表示数据范围的。
所以数据范围与刻度范围形成一定比例。
生成坐标轴的完整代码如下:
代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16const dataset = [50, 43, 120, 87, 99, 167, 142];
//scaleBand生成序数分段比例尺,x轴通常使用scaleBand
const xScale = d3.scaleBand().domain(dataset.map((d, i) => i)).range([0, xAxisWidth]).padding(0.1);
//scaleLinear生成线性比例尺,y轴通常使用这个
const yScale = d3.scaleLinear().domain([d3.max(dataset, d => d), 0]).range([0, yAxisWidth]);
//生成底部轴线
const xAxis = d3.axisBottom(xScale);
//生成左侧轴线
const yAxis = d3.axisLeft(yScale);
//设置轴线偏移位置
const gx = svg.append('g').attr('transform', `translate(${padding.left},${height - padding.bottom})`);
const gy = svg.append('g').attr('transform', `translate(${padding.left},${height - padding.bottom - yAxisWidth})`);
gx.call(xAxis);
gy.call(yAxis);
生成比例尺的方法 scaleLinear\scaleBand
这两个方法都是生成比例尺的方法,见《概述和代码》
domain range
见《概述和代码》;
除代码中外,还可以这样用:1
2const xScale = d3.scaleBand().domain([1,2,3,4]).range([11,23,25,67]);
xScale(2)//23 一一对应关系
ticks 不是一个确数
ticks设置坐标轴的刻度数量,但不是你设置几个,最终显示几个,d3会自动设置在设置数的上下。
参考《groud 与 g》的demo
domain range 的单位
如下, domain是值域,没有单位,纯粹代表数值大小;range是地域范围,单位是px,用来限定坐标轴占地面积大小。
demo 参考《groud 与 g》的demo。1
2
3var scalewidth = d3.scale.linear()
.domain([0,80])
.range([0,500])
生成对角线(直线)–d3.svg.diagonal
使用d3.svg.diagonal实现
1 | var canvas = d3.select("body") |
使用source和target定义起始和终点位置
参考上面代码。
生成折线图
概述
如图,坐标轴生成代码同《生成坐标轴》。下面的代码主要介绍生成折线图的折线。
demo地址
视频讲解
1
2
3
4
5
6//使用d3的方法来生成折线的path
const linePath = d3.line().x((d, i) => xScale(i) + padding.left + xScale.bandwidth() / 2)
.y(d => height - padding.bottom - (yScale(0) - yScale(d)));
//将数据传给上面写好的 d3生成线的path方法,避免手动写繁杂的path(而这正是d3的本职工作)
const line = svg.append('path').attr('d', linePath(dataset)).attr('stroke', '#000')
.attr('stroke-width', '3px').attr('fill', 'none');
手工写生成折线的path
svg的path手动写非常繁杂,手工写不现实,而这正是d3存在的原因之一,d3提供了丰富的方法用于生成path,也就是下面介绍到的d3.line()。
d3.line()生成折线
参考本节的《概述》
生成直方图
概述
如图,坐标轴生成代码同《生成坐标轴》。
直方图主要是绘制矩形柱状图 和 text的显示,具体下来就是如何确定柱状图的 x、y坐标,宽高度。
demo地址
视频讲解
1 | //标尺 |
设置x,y坐标、宽高度
见上面代码
svg的rect与text的运用
见上面代码
比例尺与柱状图的绘制密切联系
本示例中,柱状图的x、y坐标的确定与标尺xScale等相关联,这也是坐标轴图的套路,上面的《生成折线图》也有映射。
x、y值使用比例尺表示
见上面代码
update、enter、exit的经典运用
详见上面提到的demo地址,这个简洁demo介绍了这三种状态的渲染。
生成饼状图
饼状图的一些概念
- outerRadius 外半径
- innerRadius 内半径
- startAngle 起始角度
- endAngle 结束角度
单段饼图
1
2
3
4
5
6
7
8
9
10const svg = d3.select("#svg").append("svg").attr("width", "300").attr("height", "300");
const dataset = {startAngle:0, endAngle: Math.PI*0.75};
const arcPath = d3.arc() //弧生成器
.innerRadius(50) //设置内半径
.outerRadius(100); //设置外半径
svg.append("path")
.attr("d", arcPath(dataset)) // 用弧生成器生成弧线数据,赋值给SVG的d属性
.attr("transform", 'translate(200, 200)') // 填充颜色
.attr("stroke", '#000').attr('stroke-width', '3px') // 填充颜色
.attr("fill", 'blue') // 填充颜色
多个饼图拼成一个完整饼图
一个饼图其实是由上面说到的一个个分段的饼图拼接而成:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24const pie = d3.pie();
const dataset = [30, 10, 43, 55, 13];
const piedata = pie(dataset);
const outerRadius = 150; //外半径
const innerRadius = 0; //内半径,为0则中间没有空白
const arc = d3.arc() //弧生成器
.innerRadius(innerRadius) //设置内半径
.outerRadius(outerRadius); //设置外半径
const arcs = svg.selectAll("g") // 存放弧的容器
.data(piedata) //绑定pie数据
.enter() // 数据进入
.append("g") // 生成g
.attr("transform", "translate(" + (300 / 2) + "," + (300 / 2) + ")"); // 偏移
const color = d3.scaleOrdinal(d3.schemeCategory10);
arcs.append("path")
.attr("fill", (d, i) => color(i)) // 填充颜色
.attr("d", d => {
return arc(d)
}); // 用弧生成器生成弧线数据,赋值给SVG的d属性
arcs.append("text")
.attr("transform", d => "translate(" + arc.centroid(d) + ")") // 设置文本至扇区中心
.attr("text-anchor", "middle") //设置文本锚点
.attr("fill", "#fff") //设置文字颜色
.text(d => d.data);
获取圆弧中心点
主要运用arc的centroid API:1
2
3
4
5
6
7
8
9
10
11
12var arcFn = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
arcs.append("text")
.attr("class","MyText")
.attr("transform", function(d){
var center = arcFn.centroid(d);
return "translate(" +
center[0] * 1.4 + "," +
center[1] * 1.4 + ")";
})
熟练运用arc.centroid圆弧中心
饼图的文字或者标注线,都需要依赖圆弧中心点的运用,所以涉及到饼图时,需要重度使用圆弧中心点,以及如何凭借这个圆弧中间点,延伸画出一条中心线的技巧, 详细参考demo
demo地址与参考
arc 与 pie 关系
arc是用来画弧的,arc用来画饼图(pie)时,要设置起始角度startAngle、endAngle,为了省去这些麻烦,配合使用pie可以完美画饼图。
生成线段
完整代码如下,主要运用d3.svg.line,另外interpolate是生成不同类型连接线类型的,参考文档1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//v3.x
var svg = d3.select("body").append("svg")
.attr("width", 400)
.attr("height", 400);
var data = [
{"x": 10, "y": 200},
{"x": 50, "y": 260},
{"x": 90, "y": 120}
]
var line = d3.svg.line()
.x( function(d){ return d.x; } )
.y( function(d){ return d.y; } )
.interpolate("line");
svg.append("path")
.attr("class","MyPath")
.attr("d", line(data) )
生成线段 与 fill none
如下,没有使用fill none时,效果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26var canvas = d3.select("body")
.append("svg")
.attr("width",500)
.attr("height",500);
var data2 = [
{x : 10 , y : 20},
{x : 10 , y : 260},
{x : 250 , y : 260}
];
var group = canvas.append("g")
.attr("transform", "translate(100,200)");
var line = d3.svg.line()
.x(function(d) { return d.x;})
.y(function(d) { return d.y;});
group.selectAll("path")
.data([data2])
.enter()
.append("path")
.attr("d",line)
//.attr("fill","none")
.attr("stroke" , "#000")
.attr("stroke-width", 10 );
将上面代码的//.attr("fill","none")解注后,效果如下:
生成散点图
概述
散点图比较简单,主要代码如下,demo地址点击这里。
这里要注意的是比例尺与圆心cx cy的结合使用。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//v3.x
var xScale = d3.scale.linear()
.domain([0, 100])
.range([0,300]);
var points = svg.selectAll(".MyCircle")
.data(dataset)
.enter()
.append("circle")
.attr("class","MyCircle")
.attr("transform","translate(30,50)")
.attr("r", 10)
.attr("cx", function(d){ return xScale(d.x); })
.attr("cy", function(d){ return yScale(d.y); });
比例尺与圆心cx cy的结合使用
参考上面。
比例尺
一个简单的比例尺
1 | var linear = d3.scale.linear() |
比例尺使用情况
序数比例尺与线性比例尺用的最多;
序数比例尺用来做x轴;线性做y轴;
另外一个用的多的是量子比例尺:1
2var quantize= d3.scale.quantize().domain([0, 10]).range(['red','green','blue','yellow'])
quantize(1)//'red'
scale比例尺的作用
参考《scale比例尺的作用》
d3.event d3.mouse(this) d3.touches(this)
一般的监听和事件绑定中都可以通过d3.event获取想要的值,
具体的事件或监听中,可通过d3.mouse(this) d3.touches(this)获取值;
demo参考 《精通d3.js》的第八章。
scale比例尺的作用
没有比例尺
如下,没有比例尺时,下面两个柱形图太长了,而svg只是一个500*500的容器,大于500的,无法显示,为了自适应,引入比例尺。1
2
3
4
5
6
7
8
9
10
11var canvas = d3.select("body")
.append("svg")
.attr("width",500)
.attr("height",500);
var bar1= canvas.selectAll("rect")
.data([20,40,60,80,150])
.enter()
.append("rect")
.attr("width",function(d) { return d*8})
.attr("height",30)
.attr("y",function(d, i) { return (i+1)*65; })
使用比例尺
1 | //d3.scale.linear比例尺 |
groud 与 g
分组 与 div
groud就是分组,缩写就是 g,这也是d3上常用到的g标签。
如下面代码,加g后,可以统一设置g分组内的所有rect,所有g相当于html中的div1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27var scalewidth = d3.scale.linear()
.domain([0,80])
.range([0,500])
var axis = d3.svg.axis()
.ticks(10)
.scale(scalewidth)
var canvas = d3.select("body")
.append("svg")
.attr("width",500)
.attr("height",500)
// 这里是否加g进行分组,效果是一样的,但是加g后,可以在g上统一对下面的rect设置样式:
.append("g")
var color = d3.scale.linear()
.domain([0,140])
.range(["red","blue"])
var bar1= canvas.selectAll("rect")
.data([20,40,60,80,])
.enter()
.append("rect")
.attr("width",function(d) { return scalewidth(d)})
.attr("height",40)
.attr("fill",function(d) { return color(d)})
.attr("y",function(d) { return d * 3});
例子二 – 坐标轴是否分组
对上面的代码进行延伸,加一个坐标轴,坐标轴分组和不分组,区别很大,分组后,更容易设置样式。1
2
3
4
5
6
7
8
9
10
11
12var canvas = d3.select("body")
.append("svg")
.attr("width",500)
.attr("height",500)
.append("g")
// 不推荐写法---未将坐标轴分组,不容易设置样式:
// .call(axis)
;
// 推荐写法---将坐标轴分组,更容易设置样式:
// canvas.append("g")
// .attr("transform","translate(0,300)")
// .call(axis)
demo地址
d3.json d3.csv d3.xml d3.html d3.text
上面几种是d3请求外部文件的方式。
demo 参考
svg导出为png/svg
主要运用 d3-downloadable,demo 参考,此demo也提供了一种方案,如何自定义右键菜单,并且另存为(下载)图片。
d3黑知识
d3.event.dx 鼠标偏移量
鼠标每次偏移量,通过试验发现,偏移量基本上是三个值:-1 0 1;
这个用处非常多,通过叠加偏移量的方式,可以画一种时刻跟随鼠标的动作,比如拖动图片时,图片跟随鼠标显示。等等,拖拽饼状图 demo
d 自定义属性的传递使用
如下,在选择集中定义的customDx123属性,在arcs.append(“path”)和d3.behavior.drag()都能使用,这二者有一个共性,那就是都使用了arcs对象。
完整demo参考:拖拽饼状图 demo1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34//添加对应数目的弧组,即<g>元素
var arcs = svg.selectAll("g")
.data(piedata) //绑定转换后的数据piedata
.enter()
.append("g")
.each(function(d){
//customDx123 这个属性是自己加和命名的
d.customDx123 = width/2;
d.customDy123 = height/2;
})
.attr("transform","translate("+( width/2 )+","+ ( height/2 ) +")");
//绘制弧
arcs.append("path")
.attr("fill",function(d,i){
return color(i); //设定弧的颜色
})
.attr("d",function(d){
console.log(d.customDx123);
return arc(d); //使用弧生成器
});
//4. 交互式
var drag = d3.behavior.drag()
.origin(null)
.on("drag", dragmove);
function dragmove(d) {
//customDx123 这个属性是自己加和命名的
d.customDx123 += d3.event.dx;
d.customDy123 += d3.event.dy;
d3.select(this)
.attr("transform","translate("+d.customDx123+","+d.customDy123+")");
}
arcs.call(drag);
d3.svg.brush 区域选中
简介
效果如下,完整demo:
使用方法
1 | var xScale = d3.scale.linear() |
必须配合比例尺使用
参考上面一节代码。
监听三种事件类型
- brushstart 鼠标键按下时
- brush 鼠标键按下拖拽时
- brushend 鼠标键放开时
brush.extent 获取选中区域的x y上下限
1
2
3
4
5var extent = brush.extent();
console.log( "x方向的下限: " + extent[0][0] );
console.log( "x方向的上限: " + extent[1][0] );
console.log( "y方向的下限: " + extent[0][1] );
console.log( "y方向的上限: " + extent[1][1] );
创建 线性渐变颜色
如下,线性渐变的颜色,需要借助 url来使用。详细参考 《值域与线性渐变 – 创建 线性渐变颜色》1
2
3
4
5
6
7//常规的颜色填充
svgNode
.style("fill","red");
//线性渐变颜色填充 (线性渐变的颜色 使用 url)
svgNode
.style("fill","url(#" + linearGradient.attr("id") + ")");
利用投影函数转换经纬度成坐标
其实GeoJSON文件的地理信息也是经纬度,也是通过投影函数得到地图路径坐标的。所以可以通过投影函数获得经纬度对应的地图像素坐标。
demo参考:地图上标注地点 demo1
2
3
4
5
6
7
8
9
10var projection = d3.geo.mercator()
.center([107, 31])
.scale(600)
.translate([width/2, height/2]);
var path = d3.geo.path()
.projection(projection);
//d.log, d.lat :经纬度 计算标注点的位置
var coor = projection([d.log, d.lat]);
d3生成器
生成器就是生成svg的path路径,svg通过生成器生成的path绘制不同图形,d3提供了直线、四边形(区域)、弦、符号、折线、对角线等生成器。
svg.area 区域生成器
svg.area的原理是通过两条线 确定一个四边形;好比 两个点确定一条线的道理。
所以svg.area 都是画一条线,需要画两条线,即可确定一个四边形。
详细demo1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//创建一个区域生成器
//svg.area的原理是通过两条线 确定一个四边形;好比 两个点确定一条线的道理。
//svg.area一次性设置两个点 x0 y0; x1 y1; 通过 x 一次性设置 x0 和 y0;
var areaPath = d3.svg.area()
.x(function(d,i){ return 50 + i * 80; })
.y0(function(d,i){ return height/2; })
.y1(function(d,i){ return height/2 - d; })
// .interpolate("step");
//添加路径
svg.append("path")
.attr("d",areaPath(dataset))
.attr("stroke","black")
.attr("stroke-width","3px")
.attr("fill","yellow");
d3交互
折线图的焦点
完整demo,效果如下:
设置透明度为0的矩形
设置透明度为0的矩形,覆盖整个图,获取鼠标的焦点位置。1
2
3//获取鼠标相对于透明矩形左上角的坐标,左上角坐标为(0,0)
var mouseX = d3.mouse(this)[0] - padding.left;
var mouseY = d3.mouse(this)[1] - padding.top;
焦点位置 反射出 对应坐标值
使用函数 xScale.invert:1
var x0 = xScale.invert( mouseX );
根据坐标值 找出 实际值域内的值
实际的坐标值可能没有对应的值,那么此时应该通过逻辑找到临近的 值域内的值:1
2
3
4
5
6
7
8//对x0四舍五入,如果x0是2005.6,则返回2006;如果是2005.2,则返回2005
x0 = Math.round(x0);
//查找在原数组中x0的值,并返回索引号
var bisect = d3.bisector( function(d) { return d[0]; }).left;
var index = bisect(data, x0) ;
// 找到对应的实际定义的值
dataset[k].gdp[index]
绘制
有了坐标值,就知道所有的信息,就可以绘图了。
地图
地图数据下载地址(GeoJSON)
地图数据下载地址,这个网址下载的数据是一个zip包,里面有一个.shp文件,地图数据在这个文件中,需要解压这个数据。
从.shp提取数据
需要通过此网址从.shp文件中提取json数据的获取。提取出来的数据通常很大,需要简化使用,更加高效。
简化数据
TopoJSON
TopoJSON 是d3作者制定,表达同样的地图信息,TopoJSON文件的体积要比GeoJSON小百分之八十,当GeoJSON文件达到20M以上时,节省下来的体积更加明显,网络请求更加快速。
因此在服务器端可放置TopoJSON文件,因为文件小,请求快速,请求回来后,在前端转换为 GeoJSON 文件,然后使用GeoJSON画图。因为画图还是使用GeoJSON的。
地理路径生成器
d3.geo.path()
这是绘制地图的核心,地图基本路径都是这个;参考demo
d3.geo.graticule()
用于绘制经线和纬线;参考demo
d3.geo.circle()
用于以某一地点为中心绘制圆形圆格,一般用得少。参考demo
d3.geo.projection
用于创建投影。一般用默认的投影即可,用得少。参考demo
demo
GeoJSON demo
TopoJSON demo
地图demo讲解
值域与线性渐变
参考《值域与线性渐变》
夜光图
完整demo与效果
在svg中添加滤镜
1 | var defs = svg.append("defs"); |
标线
完整demo与效果
本例主要考察 如何创建一个标线,主要利用了 line 标签, marker-end,mark-start 属性。
完整 demo
线段的起始位置添加标记图标
本例中,线段起始位置分别添加圆点与箭头标记。1
2
3
4
5
6
7
8svg.append("line")
.attr("class","route")
.attr("x1",guilin[0])
.attr("y1",guilin[1])
.attr("x2",peking[0])
.attr("y2",peking[1])
.attr("marker-end","url(#arrow)")
.attr("marker-start","url(#startPoint)");
line的 mark-start 确定起始位置
参考上面代码
line的 marker-end 确定终点位置
参考上面代码
d3标记图标的使用
如下,就是本例效果图中的箭头图标使用方法,在defs上创建,添加marker标签:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17var defs = svg.append("defs");
var arrowMarker = defs.append("marker")
.attr("id","arrow")
.attr("markerUnits","strokeWidth")
.attr("markerWidth","12")
.attr("markerHeight","12")
.attr("viewBox","0 0 12 12")
.attr("refX","6")
.attr("refY","6")
.attr("orient","auto");
var arrow_path = "M2,2 L10,6 L2,10 L6,6 L2,2";
arrowMarker.append("path")
.attr("d",arrow_path)
.attr("fill","#000");
缩放与平移
完整demo与效果
本例主要考察 如何创建一个标线,主要利用了 line 标签, marker-end,mark-start 属性。
完整 demo
定义透明矩形,设置zoom行为
1 | var zoom = d3.behavior.zoom() |
利用投影函数的translate和scale
如上面代码
值域与线性渐变
完整demo与效果
通过值域创建颜色
实现代码
1 | //定义一个线性比例尺,将最小值和最大值之间的值映射到[0, 1] |
d3.scale.linear创建[0,1]范围的比例尺
参考上面代码
d3.interpolate(a,b)接受两个极限颜色
参考上面代码
创建 线性渐变颜色
实现代码
1 | //定义最小值和最大值对应的颜色 |
线性渐变定义在 defs 标签中
线性渐变就是上图 下面的那个渐变矩形框。
线性渐变定义在 defs 标签中。参考上面代码。
定义一个带id的linearGradient
如上,通过id来引用这个渐变颜色
渐变颜色使用url fill
参考上面代码
通过stop添加极限颜色
参考上面代码
参考
视频教程有7个,在这个网站上是能找到这七个视频的
D3.js 4.x API中文手册
D3.js 3.x API英文文档
D3.js 中文文档(包含最新版本中文文档,同时放出中英文各版本地址)



