0%

一、单个数据源时

image

image

如上图,配置如下:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//绘制图表
function drawLineChart(data) {
var xAxisArr = [],
yAxisArr = [];

$.each(data,function(key,value){
xAxisArr.push(key);
yAxisArr.push(value);
});

//计算x轴的步长 (当数据较多时,x轴会显示不下,所以需要间隔显示)
var xLen = xAxisArr.length,
maxLabelNum = 20, //x轴上最多显示的label个数,过多就会挤在一起
step = Math.round(xLen / maxLabelNum) < 1 ? 1 : Math.round(xLen / maxLabelNum);

chart = new Highcharts.Chart({
chart: {
renderTo: 'J_Diagram', //图表将要被渲染到哪个dom节点中
type: 'line', //线性显示
marginRight: 100, //图表距离右侧的距离,即上图中 "该月的流量"的显示宽度
marginBottom: 50 //图表距离下方的距离,即上图中 x轴 label的显示高度, 刚开始label一直显示不全,有一半被隐藏了, 后来发现将这个值变大就可以了
},
title: {
text: '' //图表的标题, 此为图1的设置,设为空串则不显示title, 如果不配置此项,此会显示默认的title,如图2所示
},
xAxis: {
categories: xAxisArr, //x轴显示的label,如果不设置此项,则会显示默认的数字,即1,2,...n
tickInterval: step, //x轴上显示点的间隔,默认和y轴上的点的个数相同, 但这样会造成x轴过于密集
labels: {
style: {
width: 'auto',
height: 'auto'
},
rotation: -30, //x轴label逆时针旋转30度
align: 'right' //,
// step: step //x轴上label标签的间隔,即隔多少个点显示一个label,注意如果只设置此项则x轴上的点不会变,还是会每个都显示
//,当设置了tickInterval时此值可以不设, 会和tickInterval相同
}
},
yAxis: {
title: {
text: '单位:GB'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
credits: {//图表右下角的水印,默认是highcharts.com,将其设为空串可以取消水印
text: '',
fontSize: '0'
},
tooltip: { //当鼠标移动图表上的某个点上时,显示的提示信息
formatter: function() {
return '<b>'+ this.series.name +'</b><br/>'+ this.x +': '+ this.y +'GB';
}
},
legend: { //图上 '该月的流量' 那部分的位置配置
layout: 'vertical',
align: 'right',
verticalAlign: 'top',
x: 10,
y: 100,
borderWidth: 0
},
series: [{
name: '该'+ (queryParams.showBy == 'month' ? '月' : '日') +'的流量', //图上 '该月的流量' 那部分的文字配置
data: yAxisArr
}],
plotOptions: {
series:{
marker: { //数据线上的点,默认不显示,当鼠标hover时才显示, 注意图1和图2中是enable设为true时的效果
symbol: 'triangle-down', //点的样式, 可选的值有"circle", "square", "diamond", "triangle"和"triangle-down",默认是circle
enabled: false,
states: {
hover: {
enabled: true
}
}
}
}
}
});
}

二、多个数据源时

image

如上图,有多个数据需要显示时,配置如下,不再一一讲解,请对应单一数据源的讲解和官网 API 自己理解:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
function drawLineChart(title, data){
var colors = ['#4572A7','#AA4643', '#89A54E', '#80699B', '#3D96AE', '#DB843D', '#92A8CD', '#A47D7C', '#B5CA92' ],
xAxis = [],
yAxis = [{
title: {
text: 'PV',
},
labels: {
formatter: function(){
return this.value;
},
}
}, {
title: {
text: 'UV'
},
labels: {
formatter: function(){
return this.value;
}
},
opposite: true
}],
ableskyPvData = [],
ableskyUvData = [],
wwwPvData = [],
wwwUvData = [],
orgPvData = [],
orgUvDate = [];
for(var i=0, l=data.length ; i<l; i++){
var d = new Date();
d.setTime(data[i].statsDate.time);
xAxis.push(d.getFullYear() + '-' + (d.getMonth()+1) + '-' + d.getDate());
ableskyPvData.push(data[i].totalPv);
ableskyUvData.push(data[i].totalUv);
wwwPvData.push(data[i].asDomainPv);
wwwUvData.push(data[i].asDomainUv);
orgPvData.push(data[i].orgDomainPv);
orgUvDate.push(data[i].orgDomainUv);
}
var series = [{
name: '总PV',
data: ableskyPvData,
yAxis: 0
}, {
name: '总UV',
data: ableskyUvData,
yAxis: 1
}, {
name: 'AS域名PV',
data: wwwPvData,
yAxis: 0
}, {
name: 'AS域名UV',
data: wwwUvData,
yAxis: 1
}, {
name: '机构域名PV',
data: orgPvData,
yAxis: 0
}, {
name: '机构域名UV',
data: orgUvDate,
yAxis: 1
} ];
var chart = new Highcharts.Chart({
chart: {
renderTo: 'chartContainer',
type: 'line',
zoomType: 'x'
},
title: {
text: title,
style: {
font: 'bold 16px Verdana, sans-serif'
}
},
credits: {
text: 'www.ablesky.com',
href: 'http://www.ablesky.com',
fontSize: '16px'
},
colors: colors,
xAxis: {
categories: xAxis,
labels: {
rotation: -30,
step: xAxis.length > 35? Math.round(xAxis.length / 20) : 1,
align: 'right'
}
},
yAxis: yAxis,
tooltip: {
formatter: function() {
return '<b>'+ this.series.name +'</b><br/>'+
this.x +': '+ this.y;
}
},
plotOptions: {
series:{
marker: {
enabled: false,
states: {
hover: {
enabled: true
}
}
}
}
},
series: series
});
}

附 1: 图 1 图 2 数据格式:

image

附 2: 图 3 数据格式

image

附 3:

image

webGL 的 3D 世界主要由三大要素构成:场景(scene)、相机(camera)和渲染器(renderer),三者缺一不可。渲染的原理是:我们将创建的物体,添加到场景中,再通过相机(可以理解为人的视角)渲染到渲染器,从而呈现在网页中。three.js 是 webGL 一款比较热门的类库,本文以”three.js”: “^0.77.1”为例,通过网上教程和自身实践整理成这篇笔记。

1.场景(scene)

场景就是所有物体的容器,只需创建一个。假设我们要显示一个苹果,那么就将苹果加入到场景中即可,多个物体可加入到一个场景。
构造函数:

1
var scene = new THREE.Scene();

2.相机(camera)

相机决定了场景中哪个角度的景色会显示出来,就像人的视角,分为正投影相机(THREE.OrthographicCamera)和透视投影相机(THREE.PerspectiveCamera),正投影和透视投影的区别是:透视投影有一个基本点,就是远处的物体比近处的物体小,一般我们采用透视投影相机的情况比较多。
构造函数:

1
2
3
4
5
6
7
8
9
//正投影相机
var camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
//参数详解:
//left:左平面距离相机中心点的垂直距离
//right:右平面距离相机中心点的垂直距离
//top:顶平面距离相机中心点的垂直距离
//bottom:底平面距离相机中心点的垂直距离
//near:近平面距离相机中心点的垂直距离
//far:远平面距离相机中心点的垂直距离
1
2
3
4
5
6
7
//透视投影相机
var camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
//参数详解:
//视角fov:可以理解为视角的大小,如果设置为0,相当于没有了视角,什么也看不到;如果为180,那么可以认为你的视界很广阔,但在180度的时候,往往物体很小,因为物体在你整个可视区域中的比例变小了
//近平面near:表示近处的裁面的距离,也可以认为是眼睛到近处的距离,不能为负数
//远平面far:表示远处的裁面的距离
//纵横比aspect:实际窗口的纵横比,即宽度除以高度,这个值越大,说明宽度越大

3.渲染器(renderer)

渲染器决定了渲染结果应挂接在页面的什么元素上,并以怎样的方式绘制。
构造函数:

1
2
3
4
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);//设置渲染区域大小
document.body.appendChild(renderer, domElement);//渲染在domElement并挂接到body下
renderer.render(scene, camera); //将场景通过相机视角渲染出来

如果要让物体动起来,那么我们可以利用循环渲染:requestAnimationFrame

4.光源

光是我们看见物体的关键,用 Light 表示,是所有光源的基类,底下还有很多分类,我举几个最常用的:

环境光:环境光是经过多次反射惹来的光,无法确定其最初的方向,是一种无处不在的光。环境光源放出的光线被认为来自任何方向。因此,当你仅为场景设定环境光时,所有的物体无论法向量如何,都将表现为同样的明暗程度。
构造函数:

1
THREE.AmbientLight(hex); //hex为一个16进制的颜色值

平行光:是一组没有衰减的平行的光线,类似太阳光的效果

1
THREE.DirectionalLight(hex, intensity)

点光源:由这种光源放出的光线来自同一点,且方向辐射自四面八方

1
2
3
4
THREE.PointLight(color, intensity, distance);
//color代表光的颜色
//intensity:代表光的强度,默认1.0,表示100%强度的灯光
//distance:代表光的距离,从光源所在的位置,经过distance这段距离之后,光的强度将从Intensity衰减为0,默认0.0,表示光源强度不衰减

聚光灯:这种光源的光线从一个椎体中射出,在被照射的物体上产生聚光的效果

1
2
3
THREE.SpotLight(hex, intensity, distance, angle, exponent)
//angle:聚光灯着色的角度,用弧度作为单位,这个角度是和光源的方向形成的角度
//exponent:光源模型中,衰减的一个参数,越大衰减越快

5.物体

创建一个物体可以包含多种元素,几何体,材质,纹理等,创建一个小球的简单示例:

1
2
3
4
let geometry =new THREE.SphereGeometry(3, 16, 16);   //球体
let material = new THREE.MeshPhongMaterial({color:0x48D1CC,specular:0xffffff,shininess:100}); //材质
var ball = new THREE.Mesh(geometry, material); //两者共同组成一个球体
scene.add(ball); //将球体添加至场景中

关于几何体,材质等种类非常多,具体可以参考[three.js 源码]:https://github.com/mrdoob/three.js/

6.动画

总结上述步骤:

  • 创建场景、相机、渲染器
  • 创建光源
  • 创建物体并添加至场景中
  • 渲染出场景

这样就构成了一个完整的但也是最基础的流程,网页中能看到我们创造的物体,接下来说到动画,3D 世界中的运动方式总结为三种:移动,旋转和缩放。
运动是相对的,场景动起来有两种方式:

1). 物体在坐标系中移动,相机不动

1
2
3
4
5
function animate(){
ball.position.x += 1;
renderer.render(scene, camera);
requestAnimationFrame(animation);
}

2). 相机在坐标系中移动,物体不动

1
2
3
4
5
function animate(){
camera.position.x += 1;
renderer.render(scene, camera);
requestAnimationFrame(animation);
}

地图级别控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 百度地图API功能
var map = new BMap.Map("allmap", {enableMapClick:false});//构造底图时,关闭底图可点功能

// 初始化地图,设置中心点坐标(城市名)和地图级别
map.centerAndZoom("上海",15);
map.centerAndZoom(new BMap.Point(116.404, 39.915), 11);

// 移动地图到中心点
map.panTo(new BMap.Point(113.262232,23.154345));

// 设置缩放级别
map.setZoom(14);

// 开启地图缩放
map.enableScrollWheelZoom();

// 可以拖拽
marker.enableDragging();

// 不可以拖拽
marker.disableDragging();

获取两点之间距离

1
2
3
var pointA = new BMap.Point(106.486654,29.490295);  // 创建点坐标A--大渡口区
var pointB = new BMap.Point(106.581515,29.615467); // 创建点坐标B--江北区
alert('从大渡口区到江北区的距离是:'+(map.getDistance(pointA,pointB)).toFixed(2)+' 米。'); //获取两点距离

添加 图形,点,线,面 文字

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//  marker是随着地图缩放级别,尺寸变化的
var marker = new BMap.Marker(new BMap.Point(116.404, 39.915)); // 创建点

// 折线
var polyline = new BMap.Polyline([
new BMap.Point(116.399, 39.910),
new BMap.Point(116.405, 39.920),
new BMap.Point(116.425, 39.900)
], {strokeColor: "blue", strokeWeight: 2, strokeOpacity: 0.5}); //创建折线


// 创建圆
var circle = new BMap.Circle(
point, 1500,
{strokeColor: "blue",
fillColor: "#ccc",
strokeWeight: 2,
strokeOpacity: 0.5,
fillOpacity: 0.7}
);


// 多边形
var polygon = new BMap.Polygon([
new BMap.Point(116.387112, 39.920977),
new BMap.Point(116.385243, 39.913063),
new BMap.Point(116.394226, 39.917988),
new BMap.Point(116.401772, 39.921364),
new BMap.Point(116.41248, 39.927893)
], {strokeColor: "blue", strokeWeight: 2, strokeOpacity: 0.5});


//创建矩形
var pStart = new BMap.Point(116.392214, 39.918985);
var pEnd = new BMap.Point(116.41478, 39.911901);
var rectangle = new BMap.Polygon([
new BMap.Point(pStart.lng, pStart.lat),
new BMap.Point(pEnd.lng, pStart.lat),
new BMap.Point(pEnd.lng, pEnd.lat),
new BMap.Point(pStart.lng, pEnd.lat)
], {strokeColor: "blue", strokeWeight: 2, strokeOpacity: 0.5});


// 开启(关闭)点线面的编辑(可以拖拽,移动点)
polygon.enableEditing()
polygon.disableEditing()


// 文字label
var opts = {
position : new BMap.Point(116.417854,39.921988), // 指定文本标注所在的地理位置
offset : new BMap.Size(30, -30) //设置文本偏移量
}

var label = new BMap.Label("欢迎使用百度地图,这是一个简单的文本标注哦~", opts); // 创建文本标注对象
label.setStyle({
color : "red",
fontSize : "12px",
height : "20px",
lineHeight : "20px",
fontFamily:"微软雅黑"
});

// 覆盖物显示,隐藏
marker.show()
marker.hide()

// 添加覆盖物
map.addOverlay(marker);

// 清除覆盖物
map.clearOverlays();

删除指定覆盖物

getOverlays 方法可以获取 覆盖物数组,getLabel 方法可以,获取他的 label

1
2
3
4
5
6
7
8
9
function deletePoint(){
var allOverlay = map.getOverlays();
for (var i = 0; i < allOverlay.length -1; i++){
if(allOverlay[i].getLabel().content == "我是id=1"){
map.removeOverlay(allOverlay[i]);
return false;
}
}
}

marker 自定义 icon

1
2
3
4
5
// 创建小狐狸icon
var pt = new BMap.Point(116.417, 39.909);
var myIcon = new BMap.Icon("http://lbsyun.baidu.com/jsdemo/img/fox.gif", new BMap.Size(300,157));
var marker2 = new BMap.Marker(pt,{icon:myIcon}); // 创建标注
map.addOverlay(marker2);

加载海量点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// data source
var data = {"data":[[74.438,39.006,1]]}

if (document.createElement('canvas').getContext) { // 判断当前浏览器是否支持绘制海量点
var points = []; // 添加海量点数据
for (var i = 0; i < data.data.length; i++) {
points.push(new BMap.Point(data.data[i][0], data.data[i][1]));
}
var options = {
size: BMAP_POINT_SIZE_SMALL,
shape: BMAP_POINT_SHAPE_STAR,
color: '#d340c3'
}
var pointCollection = new BMap.PointCollection(points, options); // 初始化PointCollection
pointCollection.addEventListener('click', function (e) {
alert('单击点的坐标为:' + e.point.lng + ',' + e.point.lat); // 监听点击事件
});
map.addOverlay(pointCollection); // 添加Overlay
} else {
alert('请在chrome、safari、IE8+以上浏览器查看本示例');
}

两点之间 画弧线 圆弧

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
26
27
//  引入一个库
<script type="text/javascript" src="http://api.map.baidu.com/library/CurveLine/1.5/src/CurveLine.min.js"></script>

var points = []
points.push( new BMap.Point(116.432045,39.910683) )
points.push( new BMap.Point(120.129721,30.314429) )
points.push( new BMap.Point(121.491121,25.127053) )
var curve = new BMapLib.CurveLine(points, {strokeColor:"blue", strokeWeight:3, strokeOpacity:0.5}); //创建弧线对象
map.addOverlay(curve); //添加到地图中
curve.enableEditing(); //开启编辑功能

// centre:椭圆中心点,X:横向经度,Y:纵向纬度
function add_oval(centre,x,y){
var assemble= []
var angle;
var dot;
var tangent=x/y;
for(i=0;i<36;i++)
{
angle = (2* Math.PI / 36) * i;
dot = new BMap.Point(centre.lng+Math.sin(angle)*y*tangent, centre.lat+Math.cos(angle)*y);
assemble.push(dot);
}
return assemble;
}
var oval = new BMap.Polygon(add_oval(point,0.1,0.3), {strokeColor:"blue", strokeWeight:6, strokeOpacity:0.5});
map.addOverlay(oval);

画行政区

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
26
27
28
29
30
function getBoundary(){
var bdary = new BMap.Boundary();
console.log(bdary)
bdary.get("上海市徐汇区", function(rs){ //获取行政区域
map.clearOverlays(); //清除地图覆盖物

// rs.boundaries 是一个数组,每个元素,是地理位置的字符串 "12,12;123,23"
var count = rs.boundaries.length;
if (count === 0) {
alert('未能获取当前输入行政区域');
return ;
}
console.log(rs.boundaries)
var pointArray = [];
for (var i = 0; i < count; i++) {
var ply = new BMap.Polygon(rs.boundaries[i],
{
strokeWeight: 2,
strokeColor: "#ff0000",
fillColor: 'blue'
}
); //建立多边形覆盖物
map.addOverlay(ply); //添加覆盖物
pointArray = pointArray.concat(ply.getPath());
console.log(pointArray)
}
map.setViewport(pointArray); //调整视野
addlabel();
});
}

自定义覆盖物

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// 复杂的自定义覆盖物
function ComplexCustomOverlay(point, text, mouseoverText){
this._point = point;
this._text = text;
this._overText = mouseoverText;
}

ComplexCustomOverlay.prototype = new BMap.Overlay();
ComplexCustomOverlay.prototype.initialize = function(map){
this._map = map;
var div = this._div = document.createElement("div");
div.style.position = "absolute";
div.style.zIndex = BMap.Overlay.getZIndex(this._point.lat);
div.style.backgroundColor = "#EE5D5B";
div.style.border = "1px solid #BC3B3A";
div.style.color = "white";
div.style.height = "18px";
div.style.padding = "2px";
div.style.lineHeight = "18px";
div.style.whiteSpace = "nowrap";
div.style.MozUserSelect = "none";
div.style.fontSize = "12px"
var span = this._span = document.createElement("span");
div.appendChild(span);
span.appendChild(document.createTextNode(this._text));
var that = this;

var arrow = this._arrow = document.createElement("div");
arrow.style.background = "url(http://map.baidu.com/fwmap/upload/r/map/fwmap/static/house/images/label.png) no-repeat";
arrow.style.position = "absolute";
arrow.style.width = "11px";
arrow.style.height = "10px";
arrow.style.top = "22px";
arrow.style.left = "10px";
arrow.style.overflow = "hidden";
div.appendChild(arrow);

div.onmouseover = function(){
this.style.backgroundColor = "#6BADCA";
this.style.borderColor = "#0000ff";
this.getElementsByTagName("span")[0].innerHTML = that._overText;
arrow.style.backgroundPosition = "0px -20px";
}

div.onmouseout = function(){
this.style.backgroundColor = "#EE5D5B";
this.style.borderColor = "#BC3B3A";
this.getElementsByTagName("span")[0].innerHTML = that._text;
arrow.style.backgroundPosition = "0px 0px";
}

mp.getPanes().labelPane.appendChild(div);

return div;
}
ComplexCustomOverlay.prototype.draw = function(){
var map = this._map;
var pixel = map.pointToOverlayPixel(this._point);
this._div.style.left = pixel.x - parseInt(this._arrow.style.left) + "px";
this._div.style.top = pixel.y - 30 + "px";
}

var txt = "银湖海岸城", mouseoverTxt = txt + " " + parseInt(Math.random() * 1000,10) + "套" ;

var myCompOverlay = new ComplexCustomOverlay(new BMap.Point(116.407845,39.914101), "银湖海岸城",mouseoverTxt);

mp.addOverlay(myCompOverlay);

点聚合(放大缩小)

1
2
3
4
5
<script type="text/javascript" src="http://api.map.baidu.com/library/TextIconOverlay/1.2/src/TextIconOverlay_min.js"></script>
<script type="text/javascript" src="http://api.map.baidu.com/library/MarkerClusterer/1.2/src/MarkerClusterer_min.js"></script>

// markers 是一个marker数组
var markerClusterer = new BMapLib.MarkerClusterer(map, {markers:markers});

矢量图 图标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var vectorFCArrow = new BMap.Marker(new BMap.Point(point.lng-0.01,point.lat), {
// 初始化方向向上的闭合箭头
icon: new BMap.Symbol(BMap_Symbol_SHAPE_FORWARD_CLOSED_ARROW, {
scale: 5,
strokeWeight: 1,
rotation: 0,//顺时针旋转30度
fillColor: 'red',
fillOpacity: 0.8
})
});

var vectorStar = new BMap.Marker(new BMap.Point(point.lng+0.03,point.lat-0.03), {
// 初始化五角星symbol
icon: new BMap.Symbol(BMap_Symbol_SHAPE_STAR, {
scale: 5,
fillColor: "pink",
fillOpacity: 0.8
})
});

信息弹窗

1
2
3
4
5
6
7
8
9
10
var infoWindow = new BMap.InfoWindow("地址:北京市东城区王府井大街88号乐天银泰百货八层", {
width : 200, // 信息窗口宽度
height: 100, // 信息窗口高度
title : "海底捞王府井店" , // 信息窗口标题
enableMessage:true,//设置允许信息窗发送短息
message:"亲耐滴,晚上一起吃个饭吧?戳下面的链接看下地址喔~"
});
marker.addEventListener("click", function(){
map.openInfoWindow(infoWindow,point); //开启信息窗口
});

鼠标右键

除了地图,marker 也支持加右键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var menu = new BMap.ContextMenu();
var txtMenuItem = [
{
text:'放大',
callback:function(){map.zoomIn()}
},
{
text:'缩小',
callback:function(){map.zoomOut()}
}
];
for(var i=0; i < txtMenuItem.length; i++){
menu.addItem(new BMap.MenuItem(txtMenuItem[i].text,txtMenuItem[i].callback,100));
}
map.addContextMenu(menu);

鼠标测距

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//  引入插件
<script type="text/javascript" src="http://api.map.baidu.com/library/DistanceTool/1.2/src/DistanceTool_min.js"></script>


var myDis = new BMapLib.DistanceTool(map);
map.addEventListener("load",function(){
myDis.open(); //开启鼠标测距
//myDis.close(); //关闭鼠标测距大
});

// 获取点击点坐标
map.addEventListener("click",function(e){
alert(e.point.lng + "," + e.point.lat);
});

带箭头的 line

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  /**
* 变量定义:pixelStart: 线的一端屏幕坐标,pixelEnd:线的箭头端屏幕坐标;r:选取多长距离绘制箭头(单位像素,并不是CB对应的箭头的长度,而是红色线段对应的距离);angle:箭头线(CB或者DB)与AB的夹角。
1) 首先要将AB两点的经纬度坐标转换成屏幕坐标。
2) 然后根据AB两点屏幕坐标以及r长度,计算绿色小绿点的屏幕坐标pixelTem。
3) 然后根据B点、小绿点的屏幕坐标及angle角度,计算出C,D两点的屏幕坐标。
4) 利用map的坐标转换方法,将C,D两点的屏幕坐标转成经纬度表示的坐标。
5) 利用画线方法,绘制CBD多折线。
*/


export function drawArrow(map, points) {
let polyline = new BMap.Polyline(
points, {strokeColor: "white", strokeWeight: 3});
map.addOverlay(polyline);

addArrow(polyline, 10, Math.PI / 7);

function addArrow(polyline, length, angleValue) { //绘制箭头的函数
let linePoint = polyline.getPath();//线的坐标串
console.log(linePoint);
let arrowCount = linePoint.length;
for (let i = 1; i < arrowCount; i++) { //在拐点处绘制箭头

// pointToPixel 经纬度坐标转换为像素坐标
let pixelStart = map.pointToPixel(linePoint[i - 1]);

let pixelEnd = map.pointToPixel(linePoint[i]);
let angle = angleValue;//箭头和主线的夹角
let r = length; // r/Math.sin(angle)代表箭头长度
let delta = 0; //主线斜率,垂直时无斜率
let param = 0; //代码简洁考虑
let pixelTemX, pixelTemY;//临时点坐标
let pixelX, pixelY, pixelX1, pixelY1;//箭头两个点
if (pixelEnd.x - pixelStart.x == 0) { //斜率不存在时
pixelTemX = pixelEnd.x;
if (pixelEnd.y > pixelStart.y) {
pixelTemY = pixelEnd.y - r;
} else {
pixelTemY = pixelEnd.y + r;
}
//已知直角三角形两个点坐标及其中一个角,求另外一个点坐标算法
pixelX = pixelTemX - r * Math.tan(angle);
pixelX1 = pixelTemX + r * Math.tan(angle);
pixelY = pixelY1 = pixelTemY;
} else //斜率存在时
{
delta = (pixelEnd.y - pixelStart.y) / (pixelEnd.x - pixelStart.x);
param = Math.sqrt(delta * delta + 1);

if ((pixelEnd.x - pixelStart.x) < 0) //第二、三象限
{
pixelTemX = pixelEnd.x + r / param;
pixelTemY = pixelEnd.y + delta * r / param;
} else//第一、四象限
{
pixelTemX = pixelEnd.x - r / param;
pixelTemY = pixelEnd.y - delta * r / param;
}
//已知直角三角形两个点坐标及其中一个角,求另外一个点坐标算法
pixelX = pixelTemX + Math.tan(angle) * r * delta / param;
pixelY = pixelTemY - Math.tan(angle) * r / param;

pixelX1 = pixelTemX - Math.tan(angle) * r * delta / param;
pixelY1 = pixelTemY + Math.tan(angle) * r / param;
}

let pointArrow = map.pixelToPoint(new BMap.Pixel(pixelX, pixelY));
let pointArrow1 = map.pixelToPoint(new BMap.Pixel(pixelX1, pixelY1));
let Arrow = new BMap.Polyline([
pointArrow,
linePoint[i],
pointArrow1
], {strokeColor: "white", strokeWeight: 3});
map.addOverlay(Arrow);
}
}
}

简介: 1.js 显示地图,选择容器;设置地图的中心点,并标记。 var marker, map = new AMap.

1.js 显示地图

1
<link rel="stylesheet" href="http://cache.amap.com/lbs/static/main1119.css"/><!-- 引入高德地图的样式 -->
1
2
<script src="http://webapi.amap.com/maps?v=1.3&key=aafdf4a9edda043681ff641e6d9b9ee8"></script><!-- 传入key -->
<script type="text/javascript" src="http://cache.amap.com/lbs/static/addToolbar.js"></script><!-- 引入高德地图js文件 -->
1
2
<div id="container" style="width:49%;height: 80%;"></div><!--放置地图的div  -->
<div id="container2" style="width:49%;height: 80%;"></div>
1
2
3
4
5
6
7
8
9
10
11
12
/* 选择容器;设置地图的中心点,并标记 */
var marker, map = new AMap.Map("container", {
resizeEnable: true,
center: [114.290924,30.601394],
zoom: 13
});
/* 设置地图的中心点,并标记 */
var marker2, map2 = new AMap.Map("container2", {
resizeEnable: true,
center: [114.290924,30.601394],
zoom: 13
});

2.点击获取地图经纬度坐标;创建 marker 点

1
2
3
4
5
6
7
8
9
/* 点击获取地图坐标 */
var clickEventListener = map.on('click', function(e) {
document.getElementById("xy").value = e.lnglat.getLng() + ',' + e.lnglat.getLat();
/* 点击一次新增一个标记点 */
var marker= new AMap.Marker({
map:map,
position:[e.lnglat.getLng(),e.lnglat.getLat()]
});
});

3.在地图上绘制多边形折线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//在地图上绘制折线
var editor={};
editor._polygon=(function(){
return new AMap.Polygon({
map: map,
path: lineArr,/* 这里是一个数组; */
strokeColor: "#0000ff",
strokeOpacity: 1,
strokeWeight: 3,
fillColor: "#CD2626",
fillOpacity: 0.35
});
})();
map.setFitView();
editor._polygonEditor= new AMap.PolyEditor(map, editor._polygon);

第二种绘制图形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 绘制轨迹
var polyline = new AMap.Polyline({
map: map,
path: lineArr,/* 这里是坐标数组 */
strokeColor: "red", //线颜色
strokeOpacity: 1, //线透明度
strokeWeight: 1, //线宽
strokeStyle: "solid" //线样式
});
map.setFitView();
/* 将画线的区域渲染颜色*/
var polygon = new AMap.Polygon({
map: map,
fillOpacity:0.4,
path: lineArr
});

4.开始编辑多边形;结束编辑多边形

1
2
3
4
5
6
7
8
9
10
11
12
/* 开始编辑 */
editor.startEditPolygon=function(){
editor._polygonEditor.open();
}
/*结束编辑 */
editor.closeEditPolygon=function(){
/* 结束编辑时会自动将坐标全部存在数组内
;将数组转为字符串,以#号分隔 */
var arg = lineArr.join("#");
document.getElementById("arg").value=arg+"#"; /* 这里是将数组转成的字符串存起来 */
editor._polygonEditor.close();
}

5.清除标记的方法

1
map.clearMap();

6,当时为了去除地图上的样式,起到刷新地图的效果;没有找到更好的方法,选择的重新加载一遍地图。覆盖之前的;也就是在 div 里重新加载一次;

7,自定义图标以及样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
marker= new AMap.Marker({
title:name,/* 鼠标放上去显示的标题 */
map: map,
position: arr,/* 坐标数组*/
icon: new AMap.Icon({
size: new AMap.Size(50,50), //图标大小
image: "static/img/1.png",/*图片路径 */
imageOffset: new AMap.Pixel(0,0)/* 偏移量 */
})
});

marker.setLabel({//label默认蓝框白底左上角显示,样式className为:amap-marker-label;可以在css样式里调整
offset: new AMap.Pixel(-20,20),//修改label相对于maker的位置
content:"<span style='color: red'>"+name+"</span>"/* 这里写的是html代码 */
})

1、加载地图 API

页面直接引入

1
<script charset="utf-8" src="https://map.qq.com/api/js?v=2.exp&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77"></script>

https://map.qq.com/api/js?v=2.exp&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77 网址是 API 文件的位置,v2.0 指当前使用的 API 的版本标识,key=[申请的开发密钥]

2、创建一个容纳地图的 div 容器

1
<div id="container" style="width:500px; height:300px"></div>

要使地图在网页上显示,必须为其在网页中创建一个容器。通常,我们通过创建名为 div 的元素并在浏览器的文档对象模型 (DOM) 中获取此元素的引用执行此操作。

在上述示例中,我们定义名为 “container” 的 div,并使用样式属性设置其尺寸。地图会自动使用容器尺寸调整自身的尺寸

3、地图基本对象

1
var map = new qq.maps.Map(document.getElementById('container'),myoptions);

表示地图的 JavaScript 类是 Map 类。此类的对象在页面上定义单个地图。我们使用 JavaScript new 操作符创建此类的一个新实例。当创建新的地图实例时,在页面中指定一个 DOM 节点(通常是 div 元素)作为地图的容器。HTML 节点是 JavaScript document 对象的子节点,我们通过 document.getElementById() 方法获取对此元素的引用。此代码定义一个全局变量(名为 map),并将该变量分配给新的 Map 对象。函数 Map() 称为构造函数,其定义请参考《参考手册》

myoptions 是地图的配置对象。

4、初始化地图

1
map.panTo(new qq.maps.LatLng(39.916527,116.397128));

通过 Map 构造函数创建地图之后,还需要执行一个操作,即将其初始化。初始化通过地图的 panTo() 方法完成。panTo() 方法需要 LatLng 经纬度坐标,并且调用此方法必须在对地图执行任何其它操作(包括设置地图本身的任何其它属性)之前。

LatLng 是 latitude 经度 Longitude 纬度 缩写

5、加载地图

1
<body onload="init()">

当 HTML 页面显示时,文档对象模型 (DOM) 即会扩展,接收其他外部图像和脚本并将其合并到 document 对象中。为了确保只有在完全加载页面后才将我们的地图放在页面上,我们仅在 HTML 页面的 元素收到 onload 事件后才执行构造 Map 的函数。这样做可以避免出现不可预期的行为,并使我们可以对地图绘制的方式和时间进行更多控制。body 标签的 onload 属性是事件处理程序的一个示例。腾讯 Maps Javascript API 还提供了一组事件,可供您进行处理以确定状态变化。有关详细信息,请参阅地图事件部分。

参考案例 1:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>简单地图</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<style type="text/css">
html,body{
width:100%;
height:100%;
}
*{
margin:0px;
padding:0px;
}
body, button, input, select, textarea {
font: 12px/16px Verdana, Helvetica, Arial, sans-serif;
}
p{
width:603px;
padding-top:3px;
overflow:hidden;
}
.btn{
width:142px;
}
#container{
min-width:600px;
min-height:767px;
}
</style>
<script charset="utf-8" src="https://map.qq.com/api/js?v=2.exp&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77"></script>
<script>
window.onload = function(){
//直接加载地图
//初始化地图函数 自定义函数名init
function init() {
//定义map变量 调用 qq.maps.Map() 构造函数 获取地图显示容器
var map = new qq.maps.Map(document.getElementById("container"), {
center: new qq.maps.LatLng(39.916527,116.397128), // 地图的中心地理坐标。latitude 经度 Longitude 纬度
zoom:8 // 设置缩放级别
});
}
//调用初始化函数地图,可以写在body中 <body onload="init()">
// init();

/*
* function init() {
var myLatlng = new qq.maps.LatLng(-34.397, 150.644);
var myOptions = {
zoom: 8,
center: myLatlng,
mapTypeId: qq.maps.MapTypeId.ROADMAP
}
var map = new qq.maps.Map(document.getElementById("container"), myOptions);
}
* */
}
</script>
</head>
<body onload="init()">
<!-- 定义地图显示容器 -->
<div id="container"></div>
</body>
</html>

地图事件

腾讯地图 API 具有事件接口,用户可以根据各自不同的需求添加不同的事件扩展。

目前腾讯地图提供的事件类型有两类:

  1. 用户事件(如“点击地图”鼠标事件)是从 DOM 传播到 腾讯 Maps API 中的。这些事件是独立的,并且与标准 DOM 事件不同。包括:单击事件(click)、双击事件(dblclick)、鼠标滑过事件(mouseover)、鼠标移动(mousemove)等
  2. 腾讯地图 API 特有的事件,MVC 状态更改通知反映了腾讯 Maps API 对象中的更改,并以 property_changed 惯例命名。如:缩放级别更改事件(zoom_changed)、内容更改事件(content_changed)等。

1、添加地图点击事件

1
2
3
4
5
6
7
var listener = qq.maps.event.addListener(
map,
'click',
function() {
alert('您点击了地图。');
}
);

使用 qq.maps.event.addListener() 可以将事件绑定到某一个对象上,返回一个监听者对象。方法第一个参数为被绑定事件的对象,第二个参数为事件类型,第三个参数为事件的处理方法

2、事件中获取参数

addListener() 在事件被触发时会将 DOM 事件对象传入事件的处理方法,并在事件对象中添加鼠标当前位置的地理坐标属性 LatLng

1
2
3
4
5
6
7
qq.maps.event.addListener(
map,
'click',
function(event) {
console.log('经度',event.latLng.lat,' 纬度:',event.latLng.lng);
}
);

3、移除事件

1
2
3
4
5
6
7
8
9
var listener = qq.maps.event.addListener(
map,
'click',
function() {
alert('您点击了地图。');
}
);
//移除 click 事件.
qq.maps.event.removeListener(listener);

地图控件

腾讯地图 API 控件是用户与地图交互的 UI 元素。API 提供多样的控件便于用户直接使用,并提供自定义控件 Control,最大限度满足用户需求。

目前腾讯地图 API 中提供的控件有:

  1. 导航控件
  2. 比例尺控件
  3. 自定义控件等

1、向地图添加控件

在地图上添加一个比例尺控件,并将控件放置在地图的左下角,可在代码中添加如下内容:

添加比例尺控件

1
2
3
4
5
6
7
8
9
10
11
12
map = new qq.maps.Map(
document.getElementById("container"),
{
center: new qq.maps.LatLng(39.914850, 116.403765),
zoom: 13
}
);
var scaleControl = new qq.maps.ScaleControl({
align: qq.maps.ALIGN.BOTTOM_LEFT,
margin: qq.maps.Size(85, 15),
map: map
});

地图覆盖物

覆盖物是指覆盖到地图上面的所有事物。覆盖物都有自己的地理属性,通过设置地理属性来控制覆盖物在地图中的显示位置

腾讯地图 API 中的主要包括的覆盖物有:

  • 标注
  • 折线
  • 多边形
  • 圆形
  • 信息窗口等

Echarts

一个使用 javascript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器,底层依赖矢量图形库 ZRebder,提供直观、交互丰富,可高度个性化定制的数据化图表。

  • 下载并引入 echarts
  • 准备一个具备大小的 DOM 容器
  • 初始化 echarts 实例对象
  • 指定配置项和数据
  • 将配置项设置给 echarts 实例对象

第一个 echarts 实例

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>echarts使用示例</title>
<style>
/*第二步:设置容器并设置容器大小*/
.box{
width: 600px;
height: 600px;
background-color: darkkhaki;
}
</style>
</head>
<body>
<div class="box"></div>
<!--第一步:引入-->
<script src="js/echarts.min.js"></script>
<!--第三步:进行初始化-->
<script>
/*初始化方法 echarts.init(dom容器)*/
var myChart = echarts.init(document.querySelector(".box"));
/*第四步:指定配置项和数据*/
var option = {
title: {
text: '第一个 ECharts 实例'
},
tooltip: {},
legend: {
data:['销量']
},
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
};
/*第五步:将配置项设置给echarts实例对象*/
myChart.setOption(option);
</script>
</body>
</html>

Echarts 基础配置

需要了解的主要配置:series xAxis yAxis grid tooltip title legend color

  • series
    • 系列列表。每个系列通过 type 决定自己的图表类型
    • 大白话:图标数据,指定什么类型的图标,可以多个图表重叠。
  • xAxis:直角坐标系 grid 中的 x 轴
    • boundaryGap: 坐标轴两边留白策略 true,这时候刻度只是作为分隔线,标签和数据点都会在两个刻度之间的带(band)中间。
  • yAxis:直角坐标系 grid 中的 y 轴
  • grid:直角坐标系内绘图网格。
  • title:标题组件
  • tooltip:提示框组件
  • legend:图例组件
  • color:调色盘颜色列表
    数据堆叠,同个类目轴上系列配置相同的 stack 值后 后一个系列的值会在前一个系列的值上相加。
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
option = {
// color设置我们线条的颜色 注意后面是个数组
color: [‘pink’, ‘red’, ‘green’, ‘skyblue’],
// 设置图表的标题
title: {
text: ‘折线图堆叠123’
},
// 图表的提示框组件
tooltip: {
// 触发方式
trigger: ‘axis’
},
// 图例组件
legend: {
// series里面有了 name值则 legend里面的data可以删掉
},
// 网格配置 grid可以控制线形图 柱状图 图表大小
grid: {
left: ‘3%’,
right: ‘4%’,
bottom: ‘3%’,
// 是否显示刻度标签 如果是true 就显示 否则反之
containLabel: true
},
// 工具箱组件 可以另存为图片等功能
toolbox: {
feature: {
saveAsImage: {}
}
},
// 设置x轴的相关配置
xAxis: {
type: ‘category’,
// 是否让我们的线条和坐标轴有缝隙
boundaryGap: false,
data: [‘星期一’, ‘周二’, ‘周三’, ‘周四’, ‘周五’, ‘周六’, ‘周日’]
},
// 设置y轴的相关配置
yAxis: {
type: ‘value’
},
// 系列图表配置 它决定着显示那种类型的图表
series: [
{
name: ‘邮件营销’,
type: ‘line’,
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: '联盟广告',
type: 'line',

data: [220, 182, 191, 234, 290, 330, 310]
},
{
name: '视频广告',
type: 'line',

data: [150, 232, 201, 154, 190, 330, 410]
},
{
name: '直接访问',
type: 'line',

data: [320, 332, 301, 334, 390, 330, 320]
}
]
};

theme = {
// 全图默认背景
// backgroundColor: ‘rgba(0,0,0,0)’,
// 默认色板
color: ['#ff7f50','#87cefa','#da70d6','#32cd32','#6495ed',
'#ff69b4','#ba55d3','#cd5c5c','#ffa500','#40e0d0',
'#1e90ff','#ff6347','#7b68ee','#00fa9a','#ffd700',
'#6699FF','#ff6666','#3cb371','#b8860b','#30e0e0'],

// 图表标题
title: {
x: 'left', // 水平安放位置,默认为左对齐,可选为:
// 'center' ¦ 'left' ¦ 'right'
// ¦ {number}(x坐标,单位px)
y: 'top', // 垂直安放位置,默认为全图顶端,可选为:
// 'top' ¦ 'bottom' ¦ 'center'
// ¦ {number}(y坐标,单位px)
//textAlign: null // 水平对齐方式,默认根据x设置自动调整
backgroundColor: 'rgba(0,0,0,0)',
borderColor: '#ccc', // 标题边框颜色
borderWidth: 0, // 标题边框线宽,单位px,默认为0(无边框)
padding: 5, // 标题内边距,单位px,默认各方向内边距为5,
// 接受数组分别设定上右下左边距,同css
itemGap: 10, // 主副标题纵向间隔,单位px,默认为10,
textStyle: {
fontSize: 18,
fontWeight: 'bolder',
color: '#333' // 主标题文字颜色
},
subtextStyle: {
color: '#aaa' // 副标题文字颜色
}
},

// 图例
legend: {
orient: 'horizontal', // 布局方式,默认为水平布局,可选为:
// 'horizontal' ¦ 'vertical'
x: 'center', // 水平安放位置,默认为全图居中,可选为:
// 'center' ¦ 'left' ¦ 'right'
// ¦ {number}(x坐标,单位px)
y: 'top', // 垂直安放位置,默认为全图顶端,可选为:
// 'top' ¦ 'bottom' ¦ 'center'
// ¦ {number}(y坐标,单位px)
backgroundColor: 'rgba(0,0,0,0)',
borderColor: '#ccc', // 图例边框颜色
borderWidth: 0, // 图例边框线宽,单位px,默认为0(无边框)
padding: 5, // 图例内边距,单位px,默认各方向内边距为5,
// 接受数组分别设定上右下左边距,同css
itemGap: 10, // 各个item之间的间隔,单位px,默认为10,
// 横向布局时为水平间隔,纵向布局时为纵向间隔
itemWidth: 20, // 图例图形宽度
itemHeight: 14, // 图例图形高度
textStyle: {
color: '#333' // 图例文字颜色
}
},

// 值域
dataRange: {
orient: 'vertical', // 布局方式,默认为垂直布局,可选为:
// 'horizontal' ¦ 'vertical'
x: 'left', // 水平安放位置,默认为全图左对齐,可选为:
// 'center' ¦ 'left' ¦ 'right'
// ¦ {number}(x坐标,单位px)
y: 'bottom', // 垂直安放位置,默认为全图底部,可选为:
// 'top' ¦ 'bottom' ¦ 'center'
// ¦ {number}(y坐标,单位px)
backgroundColor: 'rgba(0,0,0,0)',
borderColor: '#ccc', // 值域边框颜色
borderWidth: 0, // 值域边框线宽,单位px,默认为0(无边框)
padding: 5, // 值域内边距,单位px,默认各方向内边距为5,
// 接受数组分别设定上右下左边距,同css
itemGap: 10, // 各个item之间的间隔,单位px,默认为10,
// 横向布局时为水平间隔,纵向布局时为纵向间隔
itemWidth: 20, // 值域图形宽度,线性渐变水平布局宽度为该值 * 10
itemHeight: 14, // 值域图形高度,线性渐变垂直布局高度为该值 * 10
splitNumber: 5, // 分割段数,默认为5,为0时为线性渐变
color:['#1e90ff','#f0ffff'],//颜色
//text:['高','低'], // 文本,默认为数值文本
textStyle: {
color: '#333' // 值域文字颜色
}
},

toolbox: {
orient: 'horizontal', // 布局方式,默认为水平布局,可选为:
// 'horizontal' ¦ 'vertical'
x: 'right', // 水平安放位置,默认为全图右对齐,可选为:
// 'center' ¦ 'left' ¦ 'right'
// ¦ {number}(x坐标,单位px)
y: 'top', // 垂直安放位置,默认为全图顶端,可选为:
// 'top' ¦ 'bottom' ¦ 'center'
// ¦ {number}(y坐标,单位px)
color : ['#1e90ff','#22bb22','#4b0082','#d2691e'],
backgroundColor: 'rgba(0,0,0,0)', // 工具箱背景颜色
borderColor: '#ccc', // 工具箱边框颜色
borderWidth: 0, // 工具箱边框线宽,单位px,默认为0(无边框)
padding: 5, // 工具箱内边距,单位px,默认各方向内边距为5,
// 接受数组分别设定上右下左边距,同css
itemGap: 10, // 各个item之间的间隔,单位px,默认为10,
// 横向布局时为水平间隔,纵向布局时为纵向间隔
itemSize: 16, // 工具箱图形宽度
featureImageIcon : {}, // 自定义图片icon
featureTitle : {
mark : '辅助线开关',
markUndo : '删除辅助线',
markClear : '清空辅助线',
dataZoom : '区域缩放',
dataZoomReset : '区域缩放后退',
dataView : '数据视图',
lineChart : '折线图切换',
barChart : '柱形图切换',
restore : '还原',
saveAsImage : '保存为图片'
}
},

// 提示框
tooltip: {
trigger: 'item', // 触发类型,默认数据触发,见下图,可选为:'item' ¦ 'axis'
showDelay: 20, // 显示延迟,添加显示延迟可以避免频繁切换,单位ms
hideDelay: 100, // 隐藏延迟,单位ms
transitionDuration : 0.4, // 动画变换时间,单位s
backgroundColor: 'rgba(0,0,0,0.7)', // 提示背景颜色,默认为透明度为0.7的黑色
borderColor: '#333', // 提示边框颜色
borderRadius: 4, // 提示边框圆角,单位px,默认为4
borderWidth: 0, // 提示边框线宽,单位px,默认为0(无边框)
padding: 5, // 提示内边距,单位px,默认各方向内边距为5,
// 接受数组分别设定上右下左边距,同css
axisPointer : { // 坐标轴指示器,坐标轴触发有效
type : 'line', // 默认为直线,可选为:'line' | 'shadow'
lineStyle : { // 直线指示器样式设置
color: '#48b',
width: 2,
type: 'solid'
},
shadowStyle : { // 阴影指示器样式设置
width: 'auto', // 阴影大小
color: 'rgba(150,150,150,0.3)' // 阴影颜色
}
},
textStyle: {
color: '#fff'
}
},

// 区域缩放控制器
dataZoom: {
orient: 'horizontal', // 布局方式,默认为水平布局,可选为:
// 'horizontal' ¦ 'vertical'
// x: {number}, // 水平安放位置,默认为根据grid参数适配,可选为:
// {number}(x坐标,单位px)
// y: {number}, // 垂直安放位置,默认为根据grid参数适配,可选为:
// {number}(y坐标,单位px)
// width: {number}, // 指定宽度,横向布局时默认为根据grid参数适配
// height: {number}, // 指定高度,纵向布局时默认为根据grid参数适配
backgroundColor: 'rgba(0,0,0,0)', // 背景颜色
dataBackgroundColor: '#eee', // 数据背景颜色
fillerColor: 'rgba(144,197,237,0.2)', // 填充颜色
handleColor: 'rgba(70,130,180,0.8)' // 手柄颜色
},

// 网格
grid: {
x: 80,
y: 60,
x2: 80,
y2: 60,
// width: {totalWidth} - x - x2,
// height: {totalHeight} - y - y2,
backgroundColor: 'rgba(0,0,0,0)',
borderWidth: 1,
borderColor: '#ccc'
},

// 类目轴
categoryAxis: {
position: 'bottom', // 位置
nameLocation: 'end', // 坐标轴名字位置,支持'start' | 'end'
boundaryGap: true, // 类目起始和结束两端空白策略
axisLine: { // 坐标轴线
show: true, // 默认显示,属性show控制显示与否
lineStyle: { // 属性lineStyle控制线条样式
color: '#48b',
width: 2,
type: 'solid'
}
},
axisTick: { // 坐标轴小标记
show: true, // 属性show控制显示与否,默认不显示
interval: 'auto',
// onGap: null,
inside : false, // 控制小标记是否在grid里
length :5, // 属性length控制线长
lineStyle: { // 属性lineStyle控制线条样式
color: '#333',
width: 1
}
},
axisLabel: { // 坐标轴文本标签,详见axis.axisLabel
show: true,
interval: 'auto',
rotate: 0,
margin: 8,
// formatter: null,
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
color: '#333'
}
},
splitLine: { // 分隔线
show: true, // 默认显示,属性show控制显示与否
// onGap: null,
lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式
color: ['#ccc'],
width: 1,
type: 'solid'
}
},
splitArea: { // 分隔区域
show: false, // 默认不显示,属性show控制显示与否
// onGap: null,
areaStyle: { // 属性areaStyle(详见areaStyle)控制区域样式
color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)']
}
}
},

// 数值型坐标轴默认参数
valueAxis: {
position: 'left', // 位置
nameLocation: 'end', // 坐标轴名字位置,支持'start' | 'end'
nameTextStyle: {}, // 坐标轴文字样式,默认取全局样式
boundaryGap: [0, 0], // 数值起始和结束两端空白策略
splitNumber: 5, // 分割段数,默认为5
axisLine: { // 坐标轴线
show: true, // 默认显示,属性show控制显示与否
lineStyle: { // 属性lineStyle控制线条样式
color: '#48b',
width: 2,
type: 'solid'
}
},
axisTick: { // 坐标轴小标记
show: false, // 属性show控制显示与否,默认不显示
inside : false, // 控制小标记是否在grid里
length :5, // 属性length控制线长
lineStyle: { // 属性lineStyle控制线条样式
color: '#333',
width: 1
}
},
axisLabel: { // 坐标轴文本标签,详见axis.axisLabel
show: true,
rotate: 0,
margin: 8,
// formatter: null,
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
color: '#333'
}
},
splitLine: { // 分隔线
show: true, // 默认显示,属性show控制显示与否
lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式
color: ['#ccc'],
width: 1,
type: 'solid'
}
},
splitArea: { // 分隔区域
show: false, // 默认不显示,属性show控制显示与否
areaStyle: { // 属性areaStyle(详见areaStyle)控制区域样式
color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)']
}
}
},

polar : {
center : ['50%', '50%'], // 默认全局居中
radius : '75%',
startAngle : 90,
splitNumber : 5,
name : {
show: true,
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
color: '#333'
}
},
axisLine: { // 坐标轴线
show: true, // 默认显示,属性show控制显示与否
lineStyle: { // 属性lineStyle控制线条样式
color: '#ccc',
width: 1,
type: 'solid'
}
},
axisLabel: { // 坐标轴文本标签,详见axis.axisLabel
show: false,
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
color: '#333'
}
},
splitArea : {
show : true,
areaStyle : {
color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)']
}
},
splitLine : {
show : true,
lineStyle : {
width : 1,
color : '#ccc'
}
}
},

// 柱形图默认参数
bar: {
barMinHeight: 0, // 最小高度改为0
// barWidth: null, // 默认自适应
barGap: '30%', // 柱间距离,默认为柱形宽度的30%,可设固定值
barCategoryGap : '20%', // 类目间柱形距离,默认为类目间距的20%,可设固定值
itemStyle: {
normal: {
// color: '各异',
barBorderColor: '#fff', // 柱条边线
barBorderRadius: 0, // 柱条边线圆角,单位px,默认为0
barBorderWidth: 1, // 柱条边线线宽,单位px,默认为1
label: {
show: false
// position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
// 'inside'|'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
}
},
emphasis: {
// color: '各异',
barBorderColor: 'rgba(0,0,0,0)', // 柱条边线
barBorderRadius: 0, // 柱条边线圆角,单位px,默认为0
barBorderWidth: 1, // 柱条边线线宽,单位px,默认为1
label: {
show: false
// position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
// 'inside'|'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
}
}
}
},

// 折线图默认参数
line: {
itemStyle: {
normal: {
// color: 各异,
label: {
show: false
// position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
// 'inside'|'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
},
lineStyle: {
width: 2,
type: 'solid',
shadowColor : 'rgba(0,0,0,0)', //默认透明
shadowBlur: 5,
shadowOffsetX: 3,
shadowOffsetY: 3
}
},
emphasis: {
// color: 各异,
label: {
show: false
// position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
// 'inside'|'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
}
}
},
//smooth : false,
//symbol: null, // 拐点图形类型
symbolSize: 2, // 拐点图形大小
//symbolRotate : null, // 拐点图形旋转控制
showAllSymbol: false // 标志图形默认只有主轴显示(随主轴标签间隔隐藏策略)
},

// K线图默认参数
k: {
// barWidth : null // 默认自适应
// barMaxWidth : null // 默认自适应
itemStyle: {
normal: {
color: '#fff', // 阳线填充颜色
color0: '#00aa11', // 阴线填充颜色
lineStyle: {
width: 1,
color: '#ff3200', // 阳线边框颜色
color0: '#00aa11' // 阴线边框颜色
}
},
emphasis: {
// color: 各异,
// color0: 各异
}
}
},

// 散点图默认参数
scatter: {
//symbol: null, // 图形类型
symbolSize: 4, // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
//symbolRotate : null, // 图形旋转控制
large: false, // 大规模散点图
largeThreshold: 2000,// 大规模阀值,large为true且数据量>largeThreshold才启用大规模模式
itemStyle: {
normal: {
// color: 各异,
label: {
show: false
// position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
// 'inside'|'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
}
},
emphasis: {
// color: '各异'
label: {
show: false
// position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
// 'inside'|'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
}
}
}
},

// 雷达图默认参数
radar : {
itemStyle: {
normal: {
// color: 各异,
label: {
show: false
},
lineStyle: {
width: 2,
type: 'solid'
}
},
emphasis: {
// color: 各异,
label: {
show: false
}
}
},
//symbol: null, // 拐点图形类型
symbolSize: 2 // 可计算特性参数,空数据拖拽提示图形大小
//symbolRotate : null, // 图形旋转控制
},

// 饼图默认参数
pie: {
center : ['50%', '50%'], // 默认全局居中
radius : [0, '75%'],
clockWise : false, // 默认逆时针
startAngle: 90,
minAngle: 0, // 最小角度改为0
selectedOffset: 10, // 选中是扇区偏移量
itemStyle: {
normal: {
// color: 各异,
borderColor: '#fff',
borderWidth: 1,
label: {
show: true,
position: 'outer'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
},
labelLine: {
show: true,
length: 20,
lineStyle: {
// color: 各异,
width: 1,
type: 'solid'
}
}
},
emphasis: {
// color: 各异,
borderColor: 'rgba(0,0,0,0)',
borderWidth: 1,
label: {
show: false
// position: 'outer'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
},
labelLine: {
show: false,
length: 20,
lineStyle: {
// color: 各异,
width: 1,
type: 'solid'
}
}
}
}
},

map: {
mapType: 'china', // 各省的mapType暂时都用中文
mapLocation: {
x : 'center',
y : 'center'
// width // 自适应
// height // 自适应
},
showLegendSymbol : true, // 显示图例颜色标识(系列标识的小圆点),存在legend时生效
itemStyle: {
normal: {
// color: 各异,
borderColor: '#fff',
borderWidth: 1,
areaStyle: {
color: '#ccc'//rgba(135,206,250,0.8)
},
label: {
show: false,
textStyle: {
color: 'rgba(139,69,19,1)'
}
}
},
emphasis: { // 也是选中样式
// color: 各异,
borderColor: 'rgba(0,0,0,0)',
borderWidth: 1,
areaStyle: {
color: 'rgba(255,215,0,0.8)'
},
label: {
show: false,
textStyle: {
color: 'rgba(139,69,19,1)'
}
}
}
}
},

force : {
// 数据map到圆的半径的最小值和最大值
minRadius : 10,
maxRadius : 20,
density : 1.0,
attractiveness : 1.0,
// 初始化的随机大小位置
initSize : 300,
// 向心力因子,越大向心力越大
centripetal : 1,
// 冷却因子
coolDown : 0.99,
// 分类里如果有样式会覆盖节点默认样式
itemStyle: {
normal: {
// color: 各异,
label: {
show: false
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
},
nodeStyle : {
brushType : 'both',
color : '#f08c2e',
strokeColor : '#5182ab'
},
linkStyle : {
strokeColor : '#5182ab'
}
},
emphasis: {
// color: 各异,
label: {
show: false
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
},
nodeStyle : {},
linkStyle : {}
}
}
},

chord : {
radius : ['65%', '75%'],
center : ['50%', '50%'],
padding : 2,
sort : 'none', // can be 'none', 'ascending', 'descending'
sortSub : 'none', // can be 'none', 'ascending', 'descending'
startAngle : 90,
clockWise : false,
showScale : false,
showScaleText : false,
itemStyle : {
normal : {
label : {
show : true
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
},
lineStyle : {
width : 0,
color : '#000'
},
chordStyle : {
lineStyle : {
width : 1,
color : '#666'
}
}
},
emphasis : {
lineStyle : {
width : 0,
color : '#000'
},
chordStyle : {
lineStyle : {
width : 2,
color : '#333'
}
}
}
}
},

island: {
r: 15,
calculateStep: 0.1 // 滚轮可计算步长 0.1 = 10%
},

markPoint : {
symbol: 'pin', // 标注类型
symbolSize: 10, // 标注大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
//symbolRotate : null, // 标注旋转控制
itemStyle: {
normal: {
// color: 各异,
// borderColor: 各异, // 标注边线颜色,优先于color
borderWidth: 2, // 标注边线线宽,单位px,默认为1
label: {
show: true,
position: 'inside' // 可选为'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
}
},
emphasis: {
// color: 各异
label: {
show: true
// position: 'inside' // 'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
}
}
}
},

markLine : {
// 标线起始和结束的symbol介绍类型,如果都一样,可以直接传string
symbol: ['circle', 'arrow'],
// 标线起始和结束的symbol大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
symbolSize: [2, 4],
// 标线起始和结束的symbol旋转控制
//symbolRotate : null,
itemStyle: {
normal: {
// color: 各异, // 标线主色,线色,symbol主色
// borderColor: 随color, // 标线symbol边框颜色,优先于color
borderWidth: 2, // 标线symbol边框线宽,单位px,默认为2
label: {
show: false,
// 可选为 'start'|'end'|'left'|'right'|'top'|'bottom'
position: 'inside',
textStyle: { // 默认使用全局文本样式,详见TEXTSTYLE
color: '#333'
}
},
lineStyle: {
// color: 随borderColor, // 主色,线色,优先级高于borderColor和color
// width: 随borderWidth, // 优先于borderWidth
type: 'solid',
shadowColor : 'rgba(0,0,0,0)', //默认透明
shadowBlur: 5,
shadowOffsetX: 3,
shadowOffsetY: 3
}
},
emphasis: {
// color: 各异
label: {
show: false
// position: 'inside' // 'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
},
lineStyle : {}
}
}
},

textStyle: {
decoration: 'none',
fontFamily: 'Arial, Verdana, sans-serif',
fontFamily2: '微软雅黑', // IE8- 字体模糊并且不支持不同字体混排,额外指定一份
fontSize: 12,
fontStyle: 'normal',
fontWeight: 'normal'
},

// 默认标志图形类型列表
symbolList : [
'circle', 'rectangle', 'triangle', 'diamond',
'emptyCircle', 'emptyRectangle', 'emptyTriangle', 'emptyDiamond'
],
loadingText : 'Loading...',
// 可计算特性配置,孤岛,提示颜色
calculable: false, // 默认关闭可计算特性
calculableColor: 'rgba(255,165,0,0.6)', // 拖拽提示边框颜色
calculableHolderColor: '#ccc', // 可计算占位提示颜色
nameConnector: ' & ',
valueConnector: ' : ',
animation: true,
animationThreshold: 2500, // 动画元素阀值,产生的图形原素超过2500不出动画
addDataAnimation: true, // 动态数据接口是否开启动画效果
animationDuration: 2000,
animationEasing: 'ExponentialOut' //BounceOut

普通的图表,echarts 足够用,但是比如关系图等,echarts 定制化配置还是太麻烦。综合考量,还是 G6 方便 G6 是一个图可视化引擎。它提供了图

普通的图表,echarts 足够用,但是比如关系图等,echarts 定制化配置还是太麻烦。综合考量,还是 G6 方便

G6 是一个图可视化引擎。它提供了图的绘制、布局、分析、交互、动画等图可视化的基础能力。

1
2
3
npm install --save @antv/g6

import G6 from '@antv/g6';

G6 使用

1
2
3
4
5
6
7
8
9
<div id="mountNode"></div>

const graph = new G6.Graph({
container: 'mountNode', // String | HTMLElement,必须,在 Step 1 中创建的容器 id 或容器本身
width: 800, // Number,必须,图的宽度
height: 500, // Number,必须,图的高度
});
graph.data({nodes:[{id: 'node1'}], edges:[{source: 'node1',target: 'node2',}]});
graph.render(); // 渲染图
  • nodes 数组中包含节点对象,唯一的 id 是每个节点对象中必要的属性,x、 y 用于定位;
  • edges 数组中包含边对象,source 和 target 是每条边的必要属性,分别代表了该边的起始点 id 与 目标点 id。
  • 点和边的更多属性参见:内置的节点内置的边 教程。

整体配置

配置项 类型 选项 / 示例 默认 说明
fitView Boolean true / false false 是否将图适配到画布大小,可以防止超出画布或留白太多。
fitViewPadding Number / Array 20 / [ 20, 40, 50, 20 ] 0 画布上的四周留白宽度。
animate Boolean true / false false 是否启用图的动画。
modes Object { default: [ ‘drag-node’]} null 图上行为模式的集合(如:拖拽、缩放)。由于比较复杂,按需参见:G6 中的 Mode 教程。
defaultNode Object { type: ‘circle’, style: {}} null 节点默认的属性,包括节点的一般属性和样式属性(style)。
defaultEdge Object {type: ‘polyline’, color: ‘#000} null 边默认的属性,包括边的一般属性和样式属性(style)。
nodeStateStyles Object {hover: {……},select: {…… }} null 节点在除默认状态外,其他状态下的样式属性(style)。例如鼠标放置(hover)、选中(select)等状态。
edgeStateStyles Object {hover: {……} ,select: {…… }} null 边在除默认状态外,其他状态下的样式属性(style)。例如鼠标放置(hover)、选中(select)等状态。

这个是初始化图表配置项

图布局 Layout

当数据中没有节点位置信息,或者数据中的位置信息不满足需求时,需要借助一些布局算法对图进行布局。

相比 echarts,支持布局多很多,G6 提供了 9 种一般图的布局和 4 种树图的布局:

一般图:

  • Random Layout:随机布局;
  • Force Layout:经典力导向布局:

力导向布局:一个布局网络中,粒子与粒子之间具有引力和斥力,从初始的随机无序的布局不断演变,逐渐趋于平衡稳定的布局方式称之为力导向布局。适用于描述事物间关系,比如人物关系、计算机网络关系等。

  • Circular Layout:环形布局;
  • Radial Layout:辐射状布局;
  • MDS Layout:高维数据降维算法布局;
  • Fruchterman Layout:Fruchterman 布局,一种力导布局;
  • Dagre Layout:层次布局;
  • Concentric Layout:同心圆布局,将重要(默认以度数为度量)的节点放置在布局中心;
  • Grid Layout:格子布局,将节点有序(默认是数据顺序)排列在格子上。

树图布局:

  • Dendrogram Layout:树状布局(叶子节点布局对齐到同一层);
  • CompactBox Layout:紧凑树布局;
  • Mindmap Layout:脑图布局;
  • Intended Layout:缩进布局。

设置节点和背景

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
26
const graph = new G6.Graph({
// ...
defaultNode: {
position: 'left',
style: {
background: {
fill: '#ffffff',
stroke: 'green',
padding: [3, 2, 3, 2],
radius: 2,
lineWidth: 3,
},
},
},
defaultEdge: {
autoRotate: true,
style: {
background: {
fill: '#ffffff',
stroke: '#000000',
padding: [2, 2, 2, 2],
radius: 2,
},
},
}
})

一、简介

1、定义

D3 的全称是(Data-Driven Documents),顾名思义可以知道是一个被数据驱动的文档。 一个 JavaScript 的函数库,使用它主要是用来做数据可视化的。

2、数据可视化

把枯燥乏味复杂的数据,用简单明了的图形表示出来。

3、地位

在关于 2014 年流行的 JavaScript 图形库的调查中 D3 排名第 5,高于 JQuery。

4、D3 使用

D3 是一个 JavaScript 函数库,并不需要通常所说的“安装”。它只是一个文件,在 HTML 中引用即可。有两种方法:

(1)下载 D3.js 的文件

https://github.com/mbostock/d3/releases/download/v3.4.8/d3.zip
解压后,在 HTML 文件中包含相关的 js 文件即可

(2)还可以直接包含网络的链接,这种方法较简单:

1
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

但使用的时候要保持网络连接有效,不能在断网的情况下使用。

5、需要工具

制作网页常用的工具即可。

  • (1)记事本软件:Notepad++、Editplus、Sublime Text 等,选择自己喜欢的即可。
  • (2)浏览器:IE9 以上、Firefox、Chrome 等,推荐用 Chrome 。

二、D3.js 选择,绑定,插入数据

(一)选择元素

在 D3 中,用于 选择元素的函数有两个:

1、 d3.select():是选择所有指定元素的第一个

1
2
3
4
5
6
7
8
select("tag") 通过元素的标签名称来查找元素
实例:select("input")

select("#id")通过元素的id来查找元素
实例:select("#mydiv")

select(".myclass")通过sytle样式来查找元素
select(".mystyle")

2、 d3.selectAll():是选择指定元素的全部

这两个函数返回的结果称为 选择集。
例子:

  • (1)var body=d3.select(“body”);// 选择文档中的 body 元素
  • (2)var p1=body.select(“p”); //选择 body 中的第一个 p 元素
  • (3)var p =body.selectAll(“p”); //选择 body 中的所有 p 元素
  • (4)var svg=body.select(“svg”); //选择 body 中的 svg 元素
  • (5)var rects=svg.selectAll(“rect”); //选择 svg 中所有的 rect 元素

(二)绑定数据

D3 能将数据绑定到 DOM(文件对象模型)上,也是绑定到 文档上
D3 中是通过以下两个函数来绑定数据的:

1、datum( ):绑定一个数据到选择集上

datum()实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<head>
<meta charset="utf-8">
<title>datum</title>
</head>
<body>
<p>Apple</p>
<p>Pear</p>
<p>Banana</p>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var str = "China";
var body = d3.select("body");
var p = body.selectAll("p");
p.datum(str);
p.text(function(d, i){
return "第 "+ i + " 个元素绑定的数据是 " + d;});
</script>
</body>
</html>

结果:

1
2
3
4
5
第 0 个元素绑定的数据是 China

第 1 个元素绑定的数据是 China

第 2 个元素绑定的数据是 China

2、data( ):绑定一个数组到选择集上,数组的各项值分别与选择集的各元素绑定

data( )实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<html>
<head>
<meta charset="utf-8">
<title>datum</title>
</head>
<body>
<p>Apple</p>
<p>Pear</p>
<p>Banana</p>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var dataset = ["I like dogs","I like cats","I like snakes"];
var body = d3.select("body");
var p = body.selectAll("p");
p.data(dataset)
.text(function(d, i){
return d;
});

</script>
</body>
</html>
1
2
3
4
5
6
结果
I like dogs

I like cats

I like snakes

3、对于已经绑定了数据的选择集,还有一种选择元素的方法,

运用 function(d, i)。
参数 i 是代表索引号,可以用条件判定语句来指定执行的元素。

(三)插入元素

1、append( ):在选择集末尾插入元素

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<head>
<meta charset="utf-8">
<title>append</title>
</head>
<body>
<p>Apple</p>
<p id=myid>Pear</p>
<p>Banana</p>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
d3.select("body").append("p").text("append p element");
</script>
</body>
</html>
1
2
3
4
5
6
7
8
结果:
Apple

Pear

Banana

append p element

2、insert( ):在选择集前面插入元素

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<head>
<meta charset="utf-8">
<title>insert</title>
</head>
<body>
<p id=myid>Apple</p>
<p >Pear</p>
<p>Banana</p>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var body=d3.select("body").insert("p","#myid").text("insert p element");
var p=body.select("#myid");
</script>
</body>
1
2
3
4
5
6
7
8
9
结果:

insert p element

Apple

Pear

Banana

三、画图

(一)、画布概述

要绘图,首先需要的是一块绘图的“画布”。

HTML5 提供两种强有力的“画布”:

1、SVG(scalable vector graphics)可缩放矢量图形

(1)定义

是用于描述二维矢量图形的一种图形格式,是由万维网联盟制定的开放标准。SVG 使用 XML 格式来定义图形,除了 IE8 之前的版本外,绝大部分浏览器都支持 SVG,可将 SVG 文本直接嵌入 HTML 中显示。

(2)特点

SVG 绘制的是矢量图,因此对图像进行放大不会失真。
基于 XML,可以为每个元素添加 JavaScript 事件处理器。
每个图形均视为对象,更改对象的属性,图形也会改变。
不适合游戏应用。

2、Canvas 帆布

(1)定义

Canvas 是通过 JavaScript 来绘制 2D 图形,是 HTML5 中新增的元素。

(2)特点

绘制的是位图,图像放大后会失真。

不支持事件处理器。

能够以.png 或.jpg 格式保存图像。

适合游戏应用。

3、注意

D3 虽然没有明文规定一定要在 SVG 中绘图,但是 D3 提供了众多的 SVG 图形的生成器,它们都是只支持 SVG 的。因此,建议使用 SVG 画布。

(二)画布添加

1、 使用 D3 在 body 元素 中 添加 svg

1
2
3
4
5
6
7
var width=300;    //画布的宽度
var height=300; //画布的高度

var svg=d3.select("body") //选择文档中的body元素
.append("svg"); //添加一个SVG元素
.attr("width",width); //设定宽度
.attr("height",height); //设定高度

2、实例:绘制矩形

注意:

1
2
3
4
5
6
(1)矩形的属性,常用的有四个:
x:矩形左上角的 x 坐标
y:矩形左上角的 y 坐标
width:矩形的宽度
height:矩形的高度
(2)在 SVG 中,x 轴的正方向是水平向右,y 轴的正方向是垂直向下的。

代码:

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
26
27
28
29
30
31
32
<html>
<head>
<meta charset="utf-8">
<title>绘制矩形</title>
</head>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var width = 300; //画布的宽度300
var height = 300; //画布的高度300
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个SVG元素
.attr("width",width) //设定宽度
.attr("height",height); //设定高度
var dataset = [ 250 , 210 , 170 , 130 , 90 ]; //数据,表示矩形的宽度
var rectHeight = 25; //每个矩形所占的像素高度(包括空白处)
svg.selectAll("rect")
.data(dataset) //绑定数组
.enter() //指定选择集的enter部分
.append("rect") //添加足够数量的矩形
.attr("x",20) //取x 坐标为20
.attr("y",function(d,i){ //取y坐标
return i * rectHeight;
})
.attr("width",function(d){ //设定宽度
return d;
})
.attr("height",rectHeight-2) //设定高度
.attr("fill","steelblue"); //以铁蓝色满填充矩形
</script>
</body>
</html>

(三)比例尺

1. 为什么需要比例尺

var dataset=[250,210,170,130,90]
绘图时,直接使用 250 给矩形的宽度赋值,即矩形的宽度就是 250 个像素。 此方式非常具有局限性,如果数值过大或过小。

var dataset_1=[2.5,2.1,1.7,1.3,0.9]
对以上两个数组,绝不可能用 2.5 个像素来代表矩形的宽度,那样根本看不见;也不可能用 2500 个像素来代表矩形的宽度,因为画布没有那么长 。

比例尺是 D3 中很重要的一个概念,直接用数值的大小来代表像素不是一种好方法
将某一区域的值映射到另一区域,其大小关系不变。这就是比例尺(Scale)。

2、有哪些比例尺

在数学中,x 的范围被称为 定义域,y 的范围被称为 值域。
D3 中的比例尺,也有定义域和值域,分别被称为 domain 和 range。
开发者需要指定 domain 和 range 的范围,如此即可得到一个计算关系。

(1)线性比例尺

能将一个连续的区间,映射到另一区间。
如: var dataset=[1.2, 2.3, 0.9, 1.5, 3.3]
将 dataset 中最小的值,映射成 0;将最大的值,映射成 300。
实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<head>
<meta charset="utf-8">
<title>线性比例尺</title>
</head>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
//将 dataset 中最小的值,映射成 0;将最大的值,映射成 300。
var dataset=[1.2 , 2.3 , 0.9 , 1.5 , 3.3];

var min=d3.min(dataset);
var max=d3.max(dataset);
var linear=d3.scale.linear().domain([min,max]).range([0,300]);
alert("linear(0.9):"+linear(0.9));
alert("linear(2.3):"+linear(2.3));
alert("linear(3.3):"+linear(3.3));
</script>
</body>
</html>

其中,d3.scale.linear() 返回一个线性比例尺。
domain() 和 range() 分别设定比例尺的定义域和值域。
在这里还用到了两个函数,它们经常与比例尺一起出现: d3.max() d3.min()
这两个函数能够求数组的最大值和最小值,是 D3 提供的。
按照以上代码, 比例尺的定义域 domain 为:[0.9, 3.3] , 比例尺的值域 range 为:[0, 300] 因此,当输入 0.9 时,返回 0;当输入 3.3 时,返回 300。当输入 2.3 时呢?返回 175,这是按照线性函数的规则计算的。
有一点请大家记住: d3.scale.linear() 的返回值,是可以当做函数来使用的。因此,才有这样的用法:linear(0.9)。
结果:

1
2
3
linear(0.9);    //返回 0
linear(2.3); //返回 175
linear(3.3); //返回 300
(2)序数比例尺

用于: 定义域和值域不是连续的。
实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>
<head>
<meta charset="utf-8">
<title>序数比例尺</title>
</head>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
</body>
<script>
var index=[0,1,2,3,4];
var color=["red","blue","green","yellow","black"];
var ordinal=d3.scale.ordinal()
.domain(index)
.range(color);
alert(ordinal(0)); 返回 red
alert(ordinal(2)); //返回 green
alert(ordinal(4)); //返回 black
</script>
</html>

结果:

1
2
3
ordinal(0); //返回 red
ordinal(2); //返回 green
ordinal(4); //返回 black

3.实例: 给柱形图添加比例尺

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
26
27
28
29
30
31
32
33
34
35
36
<html>
<head>
<meta charset="utf-8">
<title>给柱形图添加比例尺</title>
</head>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var width = 300; //画布的宽度300
var height = 300; //画布的高度300
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度

var dataset=[2.5,2.1,1.7,1.3,0.9]; //数据(表示矩形的宽度)
var linear=d3.scale.linear()
.domain([0,d3.max(dataset)])
.range([0,250]);
var rectHeight = 25; //每个矩形所占的像素高度(包括空白)
svg.selectAll("rect")
.data(dataset)
.enter() //指定选择集的enter部分
.append("rect") //添加足够数量的矩形
.attr("x",20) //取x 坐标为20
.attr("y",function(d,i){ //取y坐标
return i * rectHeight;
})
.attr("width",function(d){ //设定宽度
return linear(d);
})
.attr("height",rectHeight-2) //设定高度
.attr("fill","steelblue"); // 以铁蓝色满填充矩形
</script>
</body>
</html>

(四)坐标轴

1、定义

坐标轴,是可视化图表中经常出现的一种图形,由一些列线段和刻度组成。
坐标轴在 SVG 中是没有现成的图形元素的,需要用其他的元素组合构成。
D3 提供了坐标轴的组件,如此在 SVG 画布中绘制坐标轴变得像添加一个普通元素一样简单。

2、坐标轴的构成

1
2
3
4
5
6
7
8
9
在 SVG 画布的预定义元素里,有六种基本图形:
矩形 <rect>
圆形 <circle>
椭圆 <ellipse>
线段 <line>
折线 <polyline>
多边形 <polygon>
路径 <path>
画布中的所有图形,都是由以上七种元素组成。

实例 1:给矩形增加坐标轴

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<html>
<head>
<meta charset="utf-8">
<title>给柱形图添加比例尺</title>
</head>

// 设定坐标轴的样式和位置
<style>
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}

.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>


<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var width = 300; //画布的宽度300
var height = 300; //画布的高度300
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度

var dataset=[2.5,2.1,1.7,1.3,0.9]; //数据(表示矩形的宽度)
var linear=d3.scale.linear() //定义比例尺
.domain([0,d3.max(dataset)])
.range([0,250]);


var rectHeight = 25; //每个矩形所占的像素高度(包括空白)
svg.selectAll("rect")
.data(dataset)
.enter() //指定选择集的enter部分
.append("rect") //添加足够数量的矩形
.attr("x",20) //取x 坐标为20
.attr("y",function(d,i){ //取y坐标
return i * rectHeight;
})
.attr("width",function(d){ //设定宽度
return linear(d);
})
.attr("height",rectHeight-2) //设定高度
.attr("fill","steelblue"); // 以铁蓝色满填充矩形

var axis = d3.svg.axis() //D3 中坐标轴的组件,能够在 SVG 中生成组成坐标轴的元素。
.scale(linear) //指定比例尺
.orient("bottom")//指定刻度的方向bottom 表示在坐标轴的下方显示。
.ticks(7); //指定刻度的数量

svg.append("g")
.attr("class","axis")
.attr("transform","translate(20,130)")
.call(axis);

</script>
</body>
</html>

实例 2:完整的柱形图
一个完整的柱形图包括三个部分:矩形,文字,坐标轴

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<html>
<head>
<meta charset="utf-8">
<title>完整的柱形图</title>
</head>

<style>
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}

.axis text {
font-family: sans-serif;
font-size: 11px;
}

.MyRect {
fill: steelblue;
}

.MyText {
fill: white;
text-anchor: middle;
}
</style>

<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
//1、添加 SVG 画布

//画布大小
var width = 400; //画布的宽度
var height = 400; //画布的高度

//在 body 里添加一个 SVG 画布
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度
//画布周边的空白
var padding={left:30,right:30,top:20,bottom:20}


//2、定义数据和比例尺

//定义一个数组
var dataset = [10, 20, 30, 40, 33, 24, 12, 5]; //数据(表示矩形的宽度)
//X轴的比例尺
var xScale=d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, width - padding.left - padding.right]);
//Y轴的比例尺
var yScale=d3.scale.linear()
.domain([0,d3.max(dataset)])
.range([height - padding.top - padding.bottom, 0]);

//3. 定义坐标轴

//定义X轴
var xAxis=d3.svg.axis()
.scale(xScale)
.orient("bottom");
//定义Y轴
var yAxis=d3.svg.axis()
.scale(yAxis)
.orient("left")

//矩形之间的空白处
var rectPadding=4

//添加矩形元素
var rects=svg.selectAll("MyRect")
.data(dataset)
.enter()
.append("rect")
.attr("class","MyRect")
.attr("transform","translate("+padding.left+","+padding.top+")")
.attr("x",function(d,i){
return xScale(i)+rectPadding/2;
})
.attr("y",function(d){
return yScale(d);
})
.attr("width",xScale.rangeBand()-rectPadding)
.attr("height",function(d){
return height - padding.top - padding.bottom - yScale(d);
});

//4、添加文字元素
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class","MyText")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
.attr("y",function(d){
return yScale(d);
})
.attr("dx",function(){
return (xScale.rangeBand() - rectPadding)/2;
})
.attr("dy",function(d){
return 20;
})
.text(function(d){
return d;
});

//5、添加坐标轴的元素

//添加x轴
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
//添加y轴
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);

</script>
</body>
</html>

(五)让图表动起来

D3 支持制作动态的图表。有时候,图表的变化需要缓慢的发生,以便于让用户看清楚变化的过程,也能给用户不小的友好感。

1、什么是动态图

动态的图表,是指图表在某 一时间段会发生某种变化,可能是形状、颜色、位置等,而且用户是可以看到变化的过程的。
例如,有一个圆,圆心为 (100, 100)。现在我们希望圆的 x 坐标从 100 移到 300,并且移动过程在 2 秒的时间内发生。 这种时候就需要用到动态效果,在 D3 里我们称之为过渡(transition)。

2、实现动态的方法

D3 提供了 4 个方法用于实现图形的过渡:从状态 A 变为状态 B。

1、transition()

D3 会自动对两种颜色(红色和铁蓝色)之间的颜色值(RGB 值)进行插值计算,得到过渡用的颜色值。我们无需知道中间是怎么计算的,只需要结果即可。

  • .attr(“fill”,”red”) //初始颜色为红色
  • .transition () //启动过渡
  • .attr(“fill”,”steelblue”) //终止颜色为铁蓝色

2、 duration() 指定过渡的持续时间,单位为毫秒。

3、 ease()

指定过渡的方式,常用的有:

  • linear:普通的线性变化
  • circle:慢慢地到达变换的最终状态
  • elastic:带有弹跳的到达最终状态
  • bounce:在最终状态处弹跳几次
    调用时,格式形如: ease(“bounce”)。

4、 delay() 指定延迟的时间,表示一定时间后才开始转变,单位同样为毫秒。
此函数可以对整体指定延迟,也可以对个别指定延迟。

5、实例:实现简单的动态效果

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<html>
<head>
<meta charset="utf-8">
<title>实现简单的动态效果</title>
</head>


<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var width = 700; //画布的宽度300
var height = 700; //画布的高度300
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度

//圆1
var circle1=svg.append("circle")
.attr("cx",100)
.attr("cy",100)
.attr("r",45)
.style("fill","green");

//在1秒(1000毫秒)内将圆心坐标由100变为300
circle1.transition()
.duration(1000)
.attr("cx",300);



//圆2
var circle2=svg.append("circle")
.attr("cx",100)
.attr("cy",100)
.attr("r",45)
.style("fill","green");

//在1.5秒(1500毫秒)内将圆心坐标由100变为300,
//将颜色从绿色变为红色
circle2.transition()
.duration(1500)
.attr("cx",300)
.style("fill","red");


//圆3
var circle3=svg.append("circle")
.attr("cx",100)
.attr("cy",100)
.attr("r",45)
.style("fill","green");


//在2秒(2000毫秒)内将圆心坐标由100变为300
//将颜色从绿色变为红色
//将半径从45变成25
//过渡方式采用bounce(在终点处弹跳几次)
circle3.transition()
.duration(2000)
.ease("bounce")
.attr("cx",300)
.style("fill","red")
.attr("r",25);
</script>
</body>
</html>

6、实例:给柱形图加上动态效果

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
<html>
<head>
<meta charset="utf-8">
<title>完整的柱形图</title>
</head>

<style>
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}

.axis text {
font-family: sans-serif;
font-size: 11px;
}

.MyRect {
fill: steelblue;
}

.MyText {
fill: white;
text-anchor: middle;
}
</style>

<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
//1、添加 SVG 画布

//画布大小
var width = 400; //画布的宽度
var height = 400; //画布的高度

//在 body 里添加一个 SVG 画布
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度
//画布周边的空白
var padding={left:30,right:30,top:20,bottom:20}


//2、定义数据和比例尺

//定义一个数组
var dataset = [10, 20, 30, 40, 33, 24, 12, 5]; //数据(表示矩形的宽度)
//X轴的比例尺
var xScale=d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, width - padding.left - padding.right]);
//Y轴的比例尺
var yScale=d3.scale.linear()
.domain([0,d3.max(dataset)])
.range([height - padding.top - padding.bottom, 0]);

//3. 定义坐标轴

//定义X轴
var xAxis=d3.svg.axis()
.scale(xScale)
.orient("bottom");
//定义Y轴
var yAxis=d3.svg.axis()
.scale(yAxis)
.orient("left")

//矩形之间的空白处
var rectPadding=4

//添加矩形元素
var rects=svg.selectAll(".MyRect")//选择svg中MyRect类中的所有元素
.data(dataset)
.enter()
.append("rect")
.attr("class","MyRect")
.attr("transform","translate("+padding.left+","+padding.top+")")
.attr("x",function(d,i){
return xScale(i)+rectPadding/2;
})
.attr("width",xScale.rangeBand()-rectPadding)

.attr("y",function(d){
var min=yScale.domain()[0];
return yScale(min);
})
.attr("height",function(d){
return 0;
})

.transition()
.delay(function(d,i){
return i * 200;
})

.duration(2000)
.ease("bounce")
.attr("y",function(d){
return yScale(d);
})

.attr("height",function(d){
return height - padding.top - padding.bottom - yScale(d);
})

//4、添加文字元素
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class","MyText")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
.attr("y",function(d){
return yScale(d);
})
.attr("dx",function(){
return (xScale.rangeBand() - rectPadding)/2;
})
.attr("dy",function(d){
return 20;
})
.text(function(d){
return d;
})
.attr("y",function(d){//变化前y的坐标
var min = yScale.domain()[0];
return yScale(min);
})
.transition()//过渡
.delay(function(d,i){
return i * 200;
})
.duration(2000)
.ease("bounce")
.attr("y",function(d){//变化后的y坐标
return yScale(d);
});

//5、添加坐标轴的元素

//添加x轴
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
//添加y轴
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
</script>
</body>
</html>

(七)Update、Enter、Exit

  • 1、Update、Enter、Exit 是 D3 中三个非常重要的概念,它处理的是当选择集和数据的数量关系不确定的情况。
  • 2、假设,在 body 中有三个 p 元素,有一数组 [3, 6, 9],则可以将数组中的每一项分别与一个 p 元素绑定在一起。但是,有一个问题:当数组的长度与元素数量不一致(数组长度 > 元素数量 or 数组长度 < 元素数量)时呢?这时候就需要理解 Update、Enter、Exit 的概念。
  • 3、如果数组为 [3, 6, 9, 12, 15],将此数组绑定到三个 p 元素的选择集上。可以想象,会有两个数据没有元素与之对应,这时候 D3 会建立两个空的元素与数据对应,这一部分就称为 Enter。而有元素与数据对应的部分称为 Update。如果数组为 [3],则会有两个元素没有数据绑定,那么没有数据绑定的部分被称为 Exit。示意图如下所示。
  • 4、update 部分的处理办法一般是:更新属性值 enter 部分的处理办法一般是:添加元素后,赋予属性值

(八)交互式操作

与图表的交互,指在图形元素上设置一个或多个监听器,当事件发生时,做出相应的反应。

1、什么是交互

交互,指的是用户输入了某种指令,程序接受到指令之后必须做出某种响应。对可视化图表来说,交互能使图表更加生动,能表现更多内容。
例如,拖动图表中某些图形、鼠标滑到图形上出现提示框、用触屏放大或缩小图形等等。
用户用于交互的工具一般有三种:鼠标、键盘、触屏。

2、如何添加交互

触屏常用的事件有三个:

  • touchstart:当触摸点被放在触摸屏上时。
  • touchmove:当触摸点在触摸屏上移动时。
  • touchend:当触摸点从触摸屏上拿开时。
    当某个事件被监听到时,D3 会把当前的事件存到 d3.event 对象,里面保存了当前事件的各种参数
1
2
3
4
var circle = svg.append("circle");
circle.on("click", function(){
//在这里添加交互内容
console.log(d3.event); });

实例:带有交互的柱形图

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
<html>
<head>
<meta charset="utf-8">
<title>带有交互的柱形图</title>
</head>

<style>
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}

.axis text {
font-family: sans-serif;
font-size: 11px;
}

/*.MyRect {
fill: steelblue;
}*/

.MyText {
fill: red;
text-anchor: middle;
}
</style>

<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>

//画布大小
var width = 400;
var height = 400;

//在 body 里添加一个 SVG 画布
var svg = d3.select("body")//选择d3文档中的body元素
.append("svg")
.attr("width", width)
.attr("height", height);

//画布周边的空白
var padding = {left:30, right:30, top:20, bottom:20};

//定义一个数组
var dataset = [10, 20, 30, 40, 33, 24, 12, 5];

//x轴的比例尺
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length)) //有几个矩形就有几个x刻度
.rangeRoundBands([0, width - padding.left - padding.right]);

//y轴的比例尺
var yScale = d3.scale.linear()
.domain([0,d3.max(dataset)])
.range([height - padding.top - padding.bottom, 0]);

//定义x轴
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");

//定义y轴
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");

//矩形之间的空白
var rectPadding = 4;

//添加矩形元素
var rects = svg.selectAll(".MyRect")//选择svg中MyRect类中的所有元素
.data(dataset)
.enter()
.append("rect")//添加矩形元素
.attr("class","MyRect")//清除里面的所有类,并设为自己的类myrect
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){//矩形左上角的x坐标
return xScale(i) + rectPadding/2;
} )
.attr("width", xScale.rangeBand() - rectPadding )//矩形的宽

.attr("y",function(d){//变化前的矩形左上角的y坐标
var min = yScale.domain()[0];
return yScale(min);
})
.attr("height", function(d){//变化前的矩形左上角的高
return 0;
})

.transition()//过度
.delay(function(d,i){//延迟
return i * 200;
})
.duration(2000)//变化时长
.ease("bounce")//到终点是弹跳

.attr("y",function(d){//矩形左上角的y坐标
return yScale(d);
})
.attr("height", function(d){//矩形的高
return height - padding.top - padding.bottom - yScale(d);
})
//坐标说明位置,宽高说明大小

var rects = svg.selectAll(".Myrect")//选择svg中的MyRect类中的所有元素
.attr("fill","steelblue") //填充颜色不要写在CSS里
.on("mouseover",function(d,i){//鼠标在上,颜色变黄
d3.select(this)
.attr("fill","yellow");
})
.on("mouseout",function(d,i){//鼠标移出,颜色变钢铁蓝
d3.select(this)
.transition()//500毫秒渐变
.duration(500)
.attr("fill","steelblue");
});


//添加文字元素
var texts = svg.selectAll(".MyText")//选择svg中的MyText类中的所有元素
.data(dataset)
.enter()
.append("text")//添加text元素
.attr("class","MyText")//清除里面的所有类,并设为自己的类MyText
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){//矩形左上角的x坐标
return xScale(i) + rectPadding/2;
} )
.attr("y",function(d){//矩形左上角的y坐标
return yScale(d);
})
.attr("dx",function(){//text相对于矩形的横向偏移量
return (xScale.rangeBand() - rectPadding)/2;
})
.attr("dy",function(d){//text相对于矩形的纵向偏移量
return 20;
})
.text(function(d){//text 的内容
return d;
})


.attr("y",function(d){//变化前y的坐标
var min = yScale.domain()[0];
return yScale(min);
})
.transition()//过渡
.delay(function(d,i){
return i * 200;
})
.duration(2000)
.ease("bounce")
.attr("y",function(d){//变化后的y坐标
return yScale(d);
});


//添加x轴,分组元素<g>
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);

//添加y轴,分组元素<g>
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);

</script>
</body>
</html>

(九)布局

布局不是要直接绘图,而是为了得到绘图所需的数据

1、布局是什么

布局,英文是 Layout。
从字面看,可以想到有“决定什么元素绘制在哪里”的意思。
布局的作用是:将不适合用于绘图的数据转换成了适合用于绘图的数据。

2、布局有哪些

D3 总共提供了 12 个布局:

  • 饼状图(Pie)、
  • 力导向图(Force)、
  • 弦图(Chord)、
  • 树状图(Tree)、
  • 集群图(Cluster)、
  • 捆图(Bundle)、
  • 打包图(Pack)、
  • 直方图(Histogram)、
  • 分区图(Partition)、
  • 堆栈图(Stack)、
  • 矩阵树图(Treemap)、
  • 层级图(Hierarchy)。

3、饼状图的制作

本文制作一个饼状图。在布局的应用中,最简单的就是饼状图,通过本文你将对布局有一个初步了解。

源代码:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<html>
<head>
<meta charset="utf-8">
<title>饼状图</title>
</head>
<style>
</style>
<body>
<script src="d3.min.js" charset="utf-8"></script>
<script>
var width = 400;
var height = 400;
var dataset = [ 110,30 , 10 , 43 , 55 , 13 ];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var pie = d3.layout.pie();
var piedata = pie(dataset);
var outerRadius = 150; //外半径
var innerRadius = 0; //内半径,为0则中间没有空白
var arc = d3.svg.arc() //弧生成器
.innerRadius(innerRadius) //设置内半径
.outerRadius(outerRadius); //设置外半径
var color = d3.scale.category10();
var arcs = svg.selectAll("g")
.data(piedata)
.enter()
.append("g")
.attr("transform","translate("+ (width/2) +","+ (width/2) +")");
arcs.append("path")
.attr("fill",function(d,i){
return color(i);
})
.attr("d",function(d){
return arc(d);
});
arcs.append("text")
.attr("transform",function(d){
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor","middle")
.text(function(d){
return d.data;
});
</script>
</body>
</html>

4、 力导向图的制作

力导向图中每一个节点都受到力的作用而运动,这是一种非常绚丽的图表 。
力导向图(Force-Directed Graph),是绘图的一种算法。在二维或三维空间里配置节点,节点之间用线连接,称为连线。各连线的长度几乎相等,且尽可能不相交。节点和连线都被施加了力的作用,力是根据节点和连线的相对位置计算的。根据力的作用,来计算节点和连线的运动轨迹,并不断降低它们的能量,最终达到一种能量很低的安定状态。

力导向图能表示节点之间的多对多的关系。

源代码:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<html>
<head>
<meta charset="utf-8">
<title>力导向图</title>
</head>

<style>


</style>
<body>
<script src="d3.min.js" charset="utf-8"></script>
<script>

var nodes = [ { name: "桂林" }, { name: "广州" },
{ name: "厦门" }, { name: "杭州" },
{ name: "上海" }, { name: "青岛" },
{ name: "天津" } ];

var edges = [ { source : 0 , target: 1 } , { source : 0 , target: 2 } ,
{ source : 0 , target: 3 } , { source : 1 , target: 4 } ,
{ source : 1 , target: 5 } , { source : 1 , target: 6 } ];

var width = 400;
var height = 400;


var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);

var force = d3.layout.force()
.nodes(nodes) //指定节点数组
.links(edges) //指定连线数组
.size([width,height]) //指定范围
.linkDistance(150) //指定连线长度
.charge(-400); //相互之间的作用力

force.start(); //开始作用

console.log(nodes);
console.log(edges);

//添加连线
var svg_edges = svg.selectAll("line")
.data(edges)
.enter()
.append("line")
.style("stroke","#ccc")
.style("stroke-width",1);

var color = d3.scale.category20();

//添加节点
var svg_nodes = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r",20)
.style("fill",function(d,i){
return color(i);
})
.call(force.drag); //使得节点能够拖动

//添加描述节点的文字
var svg_texts = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d){
return d.name;
});


force.on("tick", function(){ //对于每一个时间间隔

//更新连线坐标
svg_edges.attr("x1",function(d){ return d.source.x; })
.attr("y1",function(d){ return d.source.y; })
.attr("x2",function(d){ return d.target.x; })
.attr("y2",function(d){ return d.target.y; });

//更新节点坐标
svg_nodes.attr("cx",function(d){ return d.x; })
.attr("cy",function(d){ return d.y; });

//更新文字坐标
svg_texts.attr("x", function(d){ return d.x; })
.attr("y", function(d){ return d.y; });
});

</script>

</body>
</html>

5、弦图的制作

弦图是一种用于描述节点之间联系的图表。

弦图(Chord),主要用于表示两个节点之间的联系。

两点之间的连线,表示谁和谁具有联系

线的粗细表示权重

源代码:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<html>
<head>
<meta charset="utf-8">
<title>弦图</title>
<style>

.chord path {
fill-opacity: 0.67;
stroke: #000;
stroke-width: 0.5px;
}

</style>
</head>
<body>
<script src="d3.min.js"></script>
<script>

//1.定义数据
// 城市名
var city_name = [ "北京" , "上海" , "广州" , "深圳" , "香港" ];

// 城市人口的来源,如
// 北京 上海
// 北京 1000 3045
// 上海 3214 2000
// 表示北京市的人口有1000个人来自本地,有3045人是来自上海的移民,总人口为 1000 + 3045
// 上海市的人口有2000个人来自本地,有3214人是来自北京的移民,总人口为 3214 + 2000
var 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 ]
];

//2.转换数据,并输出转换后的数据
var chord_layout = d3.layout.chord()
.padding(0.03) //节点之间的间隔
.sortSubgroups(d3.descending) //排序
.matrix(population); //输入矩阵

var groups = chord_layout.groups();
var chords = chord_layout.chords();

console.log( groups );
console.log( chords );

//3.SVG,弦图,颜色函数的定义
var width = 600;
var height = 600;
var innerRadius = width/2 * 0.7;
var outerRadius = innerRadius * 1.1;

var color20 = d3.scale.category20();

var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");

//4.绘制节点(即分组,有多少个城市画多少个弧形),及绘制城市名称
var outer_arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);

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) { return 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) / 2;
d.name = city_name[i];
})
.attr("dy",".35em")
.attr("transform", function(d){
return "rotate(" + ( d.angle * 180 / Math.PI ) + ")" +
"translate(0,"+ -1.0*(outerRadius+10) +")" +
( ( d.angle > Math.PI*3/4 && d.angle < Math.PI*5/4 ) ? "rotate(180)" : "");
})
.text(function(d){
return d.name;
});


//5.绘制内部弦(即所有城市人口的来源,即有5*5=25条弧)
var inner_chord = d3.svg.chord()
.radius(innerRadius);

svg.append("g")
.attr("class", "chord")
.selectAll("path")
.data(chords)
.enter()
.append("path")
.attr("d", inner_chord )
.style("fill", function(d) { return color20(d.source.index); })
.style("opacity", 1)
.on("mouseover",function(d,i){
d3.select(this)
.style("fill","yellow");
})
.on("mouseout",function(d,i) {
d3.select(this)
.transition()
.duration(1000)
.style("fill",color20(d.source.index));
});

</script>

</body>
</html>

6、集群图的制作

集群图,是一种用于表示包含与被包含关系的图表。

源代码:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<html>
<head>
<meta charset="utf-8">
<title>集群图</title>
<style>

.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}

.node {
font: 12px sans-serif;
}

.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}

</style>
</head>
<body>
<script src="d3.min.js"></script>
<script>

var width = 500,
height = 500;

var cluster = d3.layout.cluster()
.size([width, height - 200]);

var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });

var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(40,0)");



d3.json("city.json", function(error, root) {

var nodes = cluster.nodes(root);
var links = cluster.links(nodes);

console.log(nodes);
console.log(links);

var link = svg.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class", "link")
.attr("d", diagonal);

var node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

node.append("circle")
.attr("r", 4.5);

node.append("text")
.attr("dx", function(d) { return d.children ? -8 : 8; })
.attr("dy", 3)
.style("text-anchor", function(d) { return d.children ? "end" : "start"; })
.text(function(d) { return d.name; });
});


</script>

</body>
</html>

7、树状图的制作

树状图( Tree )用于表示层级、上下级、包含与被包含关系。

源代码:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<html>
<head>
<meta charset="utf-8">
<title>树状图</title>
<style>

.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}

.node {
font: 12px sans-serif;
}

.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}

</style>
</head>
<body>
<script src="d3.min.js"></script>
<script>

var width = 500,
height = 500;

var tree = d3.layout.tree()
.size([width, height-200])
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2); });

var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });

var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(40,0)");



d3.json("city_tree.json", function(error, root) {

var nodes = tree.nodes(root);
var links = tree.links(nodes);

console.log(nodes);
console.log(links);

var link = svg.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class", "link")
.attr("d", diagonal);

var node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

node.append("circle")
.attr("r", 4.5);

node.append("text")
.attr("dx", function(d) { return d.children ? -8 : 8; })
.attr("dy", 3)
.style("text-anchor", function(d) { return d.children ? "end" : "start"; })
.text(function(d) { return d.name; });
});

</script>

</body>
</html>

8、打包图的制作

打包图( Pack ),用于表示包含与被包含的关系,也可表示各对象的权重,通常用一圆套一圆来表示前者,用圆的大小来表示后者。

源代码:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<html>
<head>
<meta charset="utf-8">
<title>Pack</title>
</head>

<style>
</style>
<body>
<script src="d3.min.js"></script>
<script>
var width = 500;
var height = 500;

var pack = d3.layout.pack()
.size([ width, height ])
.radius(20);

var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(0,0)");


d3.json("city2.json", function(error, root) {

var nodes = pack.nodes(root);
var links = pack.links(nodes);

console.log(nodes);
console.log(links);

svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("fill","rgb(31, 119, 180)")
.attr("fill-opacity","0.4")
.attr("cx",function(d){
return d.x;
})
.attr("cy",function(d){
return d.y;
})
.attr("r",function(d){
return d.r;
})
.on("mouseover",function(d,i){
d3.select(this)
.attr("fill","yellow");
})
.on("mouseout",function(d,i){
d3.select(this)
.attr("fill","rgb(31, 119, 180)");
});

svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.attr("font-size","10px")
.attr("fill","white")
.attr("fill-opacity",function(d){
if(d.depth == 2)
return "0.9";
else
return "0";
})
.attr("x",function(d){ return d.x; })
.attr("y",function(d){ return d.y; })
.attr("dx",-12)
.attr("dy",1)
.text(function(d){ return d.name; });

});

</script>

</body>
</html>

9、地图的制作

本章以中国地图为例,介绍地图的制作方法。

在数据可视化中,地图是很重要的一部分。很多情况会与地图有关联,如中国各省的人口多少,GDP 多少等,都可以和地图联系在一起。

源代码:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<html>
<head>
<meta charset="utf-8">
<title>中国地图</title>
</head>
<style>

</style>
<body>
<script src="d3.min.js"></script>
<script>
var width = 1000;
var height = 1000;

var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(0,0)");

var projection = d3.geo.mercator()
.center([107, 31])
.scale(850)
.translate([width/2, height/2]);

var path = d3.geo.path()
.projection(projection);


var color = d3.scale.category20();


d3.json("china.geojson", function(error, root) {

if (error)
return console.error(error);
console.log(root.features);

svg.selectAll("path")
.data( root.features )
.enter()
.append("path")
.attr("stroke","#000")
.attr("stroke-width",1)
.attr("fill", function(d,i){
return color(i);
})
.attr("d", path )
.on("mouseover",function(d,i){
d3.select(this)
.attr("fill","yellow");
})
.on("mouseout",function(d,i){
d3.select(this)
.attr("fill",color(i));
});

});

</script>

</body>
</html>

pm2 是一个带有负载均衡功能的 Node 应用的进程管理器.当你要把你的独立代码利用全部的服务器上的所有 CPU,并保证进程永远都活着,0 秒的重载, PM2 是完美的,下面我们来看 pm2 常用的命令用法介绍吧。

PM2 (github 上的源码)是开源的基于 Nodejs 的进程管理器,包括守护进程,监控,日志的一整套完整的功能,基本是 Nodejs 应用程序不二的守护进程选择,事实上它并不仅仅可以启动 Nodejs 的程序,只要是一般的脚本的程序它同样可以胜任。

以下是 pm2 常用的命令行:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
$ pm2 start app.js              # 启动app.js应用程序

$ pm2 start app.js -i 4 # cluster mode 模式启动4个app.js的应用实例 # 4个应用程序会自动进行负载均衡

$ pm2 start app.js --name="api" # 启动应用程序并命名为 "api"

$ pm2 start app.js --watch # 当文件变化时自动重启应用

$ pm2 start script.sh # 启动 bash 脚本


$ pm2 list # 列表 PM2 启动的所有的应用程序

$ pm2 monit # 显示每个应用程序的CPU和内存占用情况

$ pm2 show [app-name] # 显示应用程序的所有信息


$ pm2 logs # 显示所有应用程序的日志

$ pm2 logs [app-name] # 显示指定应用程序的日志

$ pm2 flush


$ pm2 stop all # 停止所有的应用程序

$ pm2 stop 0 # 停止 id为 0的指定应用程序

$ pm2 restart all # 重启所有应用

$ pm2 reload all # 重启 cluster mode下的所有应用

$ pm2 gracefulReload all # Graceful reload all apps in cluster mode

$ pm2 delete all # 关闭并删除所有应用

$ pm2 delete 0 # 删除指定应用 id 0

$ pm2 scale api 10 # 把名字叫api的应用扩展到10个实例

$ pm2 reset [app-name] # 重置重启数量


$ pm2 startup # 创建开机自启动命令

$ pm2 save # 保存当前应用列表

$ pm2 resurrect # 重新加载保存的应用列表

$ pm2 update # Save processes, kill PM2 and restore processes

$ pm2 generate # Generate a sample json configuration file


$ pm2 deploy app.json prod setup # Setup "prod" remote server

$ pm2 deploy app.json prod # Update "prod" remote server

$ pm2 deploy app.json prod revert 2 # Revert "prod" remote server by 2


$ pm2 module:generate [name] # Generate sample module with name [name]

$ pm2 install pm2-logrotate # Install module (here a log rotation system)

$ pm2 uninstall pm2-logrotate # Uninstall module

$ pm2 publish # Increment version, git push and npm publish

一.安装

全局安装,简直不能更简单。

1
​npm install -g pm2

pm2 安装好后,会自动创建下面目录。看文件名基本就知道干嘛的。

1
2
3
4
5
6
7
8
$HOME/.pm2 will contain all PM2 related files
$HOME/.pm2/logs will contain all applications logs
$HOME/.pm2/pids will contain all applications pids
$HOME/.pm2/pm2.log PM2 logs
$HOME/.pm2/pm2.pid PM2 pid
$HOME/.pm2/rpc.sock Socket file for remote commands
$HOME/.pm2/pub.sock Socket file for publishable events
$HOME/.pm2/conf.js PM2 Configuration

二.入门教程

挑我们最爱的 express 应用来举例。一般我们都是通过 npm start 启动应用,其实就是调用 node ./bin/www。那么,换成 pm2 就是

1
pm2 start ./bin/www –watch

注意,这里用了–watch 参数,意味着当你的 express 应用代码发生变化时,pm2 会帮你重启服务,多贴心。

参数说明:

1
2
3
4
5
6
7
--watch:监听应用目录的变化,一旦发生变化,自动重启。如果要精确监听、不监听的目录,最好通过配置文件。
-i --instances:启用多少个实例,可用于负载均衡。如果-i 0或者-i max,则根据当前机器核数确定实例数目。
--ignore-watch:排除监听的目录/文件,可以是特定的文件名,也可以是正则。比如--ignore-watch="test node_modules "some scripts""
-n --name:应用的名称。查看应用信息的时候可以用到。
-o --output <path>:标准输出日志文件的路径。
-e --error <path>:错误输出日志文件的路径。
--interpreter <interpreter>:the interpreter pm2 should use for executing app (bash, python...)。比如你用的coffee script来编写应用。

三.重启

1
pm2 restart app.js

四.停止

停止特定的应用。可以先通过 pm2 list 获取应用的名字(–name 指定的)或者进程 id。

1
pm2 stop app_name|app_id

如果要停止所有应用,可以

1
pm2 stop all

五.查看进程状态

1
pm2 list

六.配置文件

配置文件里的设置项,跟命令行参数基本是一一对应的。可以选择 yaml 或者 json 文件,就看个人喜好了。json 格式的配置文件,pm2 当作普通的 js 文件来处理,所以可以在里面添加注释或者编写代码,这对于动态调整配置很有好处。
  如果启动的时候指定了配置文件,那么命令行参数会被忽略。(个别参数除外,比如–env)

举个简单例子,完整配置说明请参考官方文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
“name” : “fis-receiver”, // 应用名称
“script” : “./bin/www”, // 实际启动脚本
“cwd” : “./”, // 当前工作路径
“watch”: [ // 监控变化的目录,一旦变化,自动重启
“bin”,
“routers”
],
“ignore_watch” : [
// 从监控目录中排除
“node_modules”,
“logs”,
“public”
],
“watch_options”: {
“followSymlinks”: false
},
“error_file” : “./logs/app-err.log”, // 错误日志路径
“out_file” : “./logs/app-out.log”, // 普通日志路径
“env”: {
“NODE_ENV”: “production” // 环境参数,当前指定为生产环境
}
}

七.pm2 start app.js –watch

这里是监控整个项目的文件,如果只想监听指定文件和目录,建议通过配置文件的 watch、ignore_watch 字段来设置。

八.环境切换

在实际项目开发中,我们的应用经常需要在多个环境下部署,比如开发环境、测试环境、生产环境等。在不同环境下,有时候配置项会有差异,比如链接的数据库地址不同等。

对于这种场景,pm2 也是可以很好支持的。首先通过在配置文件中通过 env_xx 来声明不同环境的配置,然后在启动应用时,通过–env 参数指定运行的环境。

首先,在配置文件中,通过 env 选项声明多个环境配置如下:

1
2
3
“env”: { “NODE_ENV”: “production”, “REMOTE_ADDR”: “http://www.example.com/” },
​”env_dev”: { “NODE_ENV”: “development”, “REMOTE_ADDR”: “http://wdev.example.com/” },
​”env_test”: { “NODE_ENV”: “test”, “REMOTE_ADDR”: “http://wtest.example.com/” }

env 为默认的环境配置(生产环境),env_dev、env_test 则分别是开发、测试环境。可以看到,不同环境下的 NODE_ENV、REMOTE_ADDR 字段的值是不同的。

在应用中,可以通过 process.env.REMOTE_ADDR 等来读取配置中声明的变量。

假设通过下面启动脚本(开发环境),那么,此时 process.env.REMOTE_ADDR 的值就是相应的 http://wdev.example.com/。

1
pm2 start app.js –env dev

九.负载均衡

命令如下,表示开启三个进程。如果-i 0,则会根据机器当前核数自动开启尽可能多的进程。

1
2
pm2 start app.js -i 3 # 开启三个进程
​pm2 start app.js -i max # 根据机器CPU核数,开启对应数目的进程

十.日志查看

除了可以打开日志文件查看日志外,还可以通过 pm2 logs 来查看实时日志。这点对于线上问题排查非常重要。

比如某个 node 服务突然异常重启了,那么可以通过 pm2 提供的日志工具来查看实时日志,看是不是脚本出错之类导致的异常重启。

十一.开机自动启动

可以通过 pm2 startup 来实现开机自启动。细节可参考。大致流程如下:

  1. 通过 pm2 save 保存当前进程状态。
  2. 通过 pm2 startup [platform]生成开机自启动的命令。(记得查看控制台输出)
  3. 将步骤 2 生成的命令,粘贴到控制台进行,搞定。

假设是在 centos 下,那么运行如下命令,搞定。强烈建议运行完成之后,重启机器,看是否设置成功。

1
2
3
4
5
6
7
8
9
10
[root@iZ94wb7tioqZ option_analysis]# pm2 save
​[root@iZ94wb7tioqZ option_analysis]# pm2 startup centos
​[PM2] Generating system init script in /etc/init.d/pm2-init.sh
[PM2] Making script booting at startup…
[PM2] /var/lock/subsys/pm2-init.sh lockfile has been added
[PM2] -centos- Using the command:
su -c “chmod +x /etc/init.d/pm2-init.sh; chkconfig –add pm2-init.sh”
​[PM2] Done.
[root@iZ94wb7tioqZ option_analysis]# pm2 save
​[PM2] Dumping processes

十二.监控(monitor)

运行如下命令,查看当前通过 pm2 运行的进程的状态。

1
pm2 monit

看到类似输出

1
2
3
4
5
6
7
8
9
10
[root@oneday-dev0 server]# pm2 monit
​ PM2 monitoring (To go further check out https://app.keymetrics.io) [ ] 0 %
​PM2 monitoring (To go further check o[||||||||||||||| ] 196.285 MB
​● fis-receiver [ ] 0 % [1] [fork_mode] [||||| ] 65.773 MB
​● www [ ] 0 % [2] [fork_mode] [||||| ] 74.426 MB
​● oc-server [ ] 0 % [3] [fork_mode] [|||| ] 57.801 MB
​● pm2-http-interface [ ] stopped
[4] [fork_mode] [ ] 0 B
​● start-production
[5] [fork_mode]

十三.内存使用超过上限自动重启

如果想要你的应用,在超过使用内存上限后自动重启,那么可以加上–max-memory-restart 参数。(有对应的配置项)

1
pm2 start big-array.js –max-memory-restart 20M

十四.pm2 + nginx

无非就是在 nginx 上做个反向代理配置,直接贴配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
upstream my_nodejs_upstream {
server 127.0.0.1:3001;
}
server {
listen 80;
server_name my_nodejs_server;
root /home/www/project_root;
location / {
proxy_set_header X-Forwarded-For proxy_add_x_forwarded_for;
proxy_set_header Hostproxy_add_x_forwarded_for;
proxy_set_header Hosthttp_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
proxy_max_temp_file_size 0;
proxy_pass http://my_nodejs_upstream/;
proxy_redirect off;
proxy_read_timeout 240s;
}
}

官方文档:http://pm2.keymetrics.io/docs/tutorials/pm2-nginx-production-setup

一、docker 的命令的解释

1、命令解释

1
2
3
4
5
6
docker run -d -p 80:80 nginx

run (创建并运行一个容器)
-d 放在后台
-p 端口映射
nginx docker镜像的名字

2、配置 docker 镜像加速

1
2
3
4
5
6
vi /etc/docker/daemon.json
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}

systemctl restart docker

3、docker 容器是一种软件的打包技术,接下来我们体验一下

1、传统编译安装 nginx:

1
2
3
4
5
6
7
官网下载Nginx源码包wget
tar解压源码包
创建Nginx用户
安装依赖包
编译安装三部曲configure,make,make install
修改nginx配置文件
启动nginx

2、docker 容器

1
docker run -d -p 80:80 nginx

二、镜像常用命令

1、搜索镜像:

1
docker search

选镜像的建议:

  1. 优先考虑官方
  2. stars 数量多

2、获取镜像:

1
docker pull(push)

镜像加速器:阿里云加速器,daocloud 加速器,中科大加速器,Docker 中国官方镜像加速:https://registry.docker-cn.com

1
2
docker pull centos:6.8(没有指定版本,默认会下载最新版)
docker pull daocloud.io/huangzhichong/alpine-cn:latest

扩展:查询 docker 镜像所有版本 https://hub.docker.com/r/library/

3、查看删除导出

1、查看镜像

1
docker images

2、删除镜像

1
docker rmi 例子:docker image rm centos:latest

3、导出镜像

1
docker save 例子:docker image save centos > docker-centos7.4.tar.gz<br>docker save mysql:5.7> mysql-5.7.tar.gz

4、导入镜像

1
docker load 例子:docker load -i docker-centos7.4.tar.gz

三、容器常用命令

1、运行一个容器

1
2
3
4
5
6
7
8
docker run -d -p 80:80 nginx:latest
run (创建并运行一个容器)
-d 放在后台
-p 端口映射
nginx:latest docker镜像的名字和版本
还有很多其他的参数

docker run == docker create + docker start

2、进入到容器

进入容器的目的:排错,调试

进入容器的方法:

1
2
3
4
5
6
7
8
9
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
例子:
docker exec -it 容器id或容器名字 /bin/bash

docker attach [OPTIONS] CONTAINER
例子:
docker attach 容器id或容器名字

nsenter(安装yum install -y util-linux 弃用)

3、停止删除容器命令

1、停止容器

1
docker stop CONTAINER_ID

2、杀死容器

1
docker kill container_name

3、查看容器列表

1
2
3
4
docker ps
docker ps –a

docker ps –a -l

4、删除容器

1
docker rm

5、批量删除容器

1
docker rm -f `docker ps -a -q`

4、要获取所有容器名称及其 IP 地址只需一个命令。

1
docker inspect -f '{{.Name}} - {{.NetworkSettings.IPAddress }}' $(docker ps -aq)

如果使用 docker-compose 命令将是:

1
docker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq)

5、显示容器的所有 IP

1
docker inspect --format='{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq)

四、容器网络访问常用命令

1
2
3
4
5
6
7
8
docker run -it --name centos6 centos:6.8 /bin/bash
-it 分配交互式的终端
--name 指定容器的名字
/bin/sh 覆盖容器的初始命令

--cpus 指定cpu的数量
--memory 限定内存的大小
-h 指定容器的主机名

docker 的本质是:在隔离的环境运行的一个进程

所以:docker 容器内的第一个进程必须一直处于前台运行的状态(必须夯住),否则这个容器,就会处于退出状态!

1、运行容器为什么要使用端口映射?

默认情况下,容器使用的 ip 是 172.17.0.0/16 网段的,外界的用户只能访问宿主机的 10.0.0.0/24 网段,

无法访问 172.17.0.0/16 网段。我们运行容器的目的:是希望运行在容器中的服务,能够被外界访问,

这里就涉及到了外网 10.0.0.0/24 到容器内网 172.17.0.0/16 网段的转换,所以需要做端口映射

2、指定映射(docker 自动添加一条 iptables 规则实现端口映射)

1
2
3
4
5
-p hostPort:containerPort
-p ip:hostPort:containerPort
-p ip::containerPort(随机端口)
-p hostPort:containerPort:udp
-p 81:80 -p 443:443 可以指定多个-p 

3、随机映射

1
docker run -P (随机端口)

五、数据卷常用命令

1、数据卷的作用:

  • 持久化容器运行过程中产生的数据文件
  • 实现多个容器间的文件共享。

正常情况下,删除容器,容器中所有的文件也会被删除

2、数据卷常用命令

1、创建一个数据卷

1
docker volume create

2、查看数据卷列表

1
docker volume ls

3、删除一个数据卷

1
docker volume rm

4、查看一个数据卷的属性

1
docker volume inspect

3、docker 数据卷运用一

1
2
3
4
5
6
7
8
9
10
11
docker volume create luoahong

docker run -d -p 80:80 -v luoahong:/usr/share/nginx/html nginx:latest

cd /var/lib/docker/volumes/luoahong/_data/
rm -f *
wget https://www.qstack.com.cn/xiaoniaofeifei.zip
unzip xiaoniaofeifei.zip

docker run -d -p 81:80 -v luoahong:/usr/share/nginx/html nginx:latest
docker run -d -p 82:80 -v luoahong:/usr/share/nginx/html nginx:latest

4、把宿主机的目录挂载到容器中

1
2
3
4
5
cd /opt
mkdir xiaoniao
cd xiaoniao/
wget https://www.qstack.com.cn/xiaoniaofeifei.zip
unzip xiaoniaofeifei.zip

重点

1
docker run -d -p 83:80 -v /opt/xiaoniao:/usr/share/nginx/html nginx:latest