d3 概述
|
```text
官网
https://d3js.org/
历史版本下载,后面换为自己想要版本号
https://registry.npmjs.org/d3/-/d3-7.4.4.tgz
```
|
|
```js
yarn add d3
import * as d3 from "d3";
import {select, selectAll} from "d3";
import {mean, median} from "d3-array";
```
D3 in React
```jsx
import * as d3 from "d3";
export default function LinePlot({
data,
width = 640,
height = 400,
marginTop = 20,
marginRight = 20,
marginBottom = 20,
marginLeft = 20
}) {
const x = d3.scaleLinear([0, data.length - 1], [marginLeft, width - marginRight]);
const y = d3.scaleLinear(d3.extent(data), [height - marginBottom, marginTop]);
const line = d3.line((d, i) => x(i), y);
return (
<svg width={width} height={height}>
<path fill="none" stroke="currentColor" stroke-width="1.5" d={line(data)} />
<g fill="white" stroke="currentColor" stroke-width="1.5">
{data.map((d, i) => (<circle key={i} cx={x(i)} cy={y(d)} r="2.5" />))}
</g>
</svg>
);
}
```
|
|
HTML直接引入
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>import d3</title>
<link rel='shortcut icon' href='#'></link>
<script type="text/javascript" src="static/d3.744/d3.js"></script>
</head>
<body>
<svg width="50" height="50">
<circle cx="25" cy="25" r="22" fill="pink" stroke="gray" stroke-width="2"/>
</svg>
<script type="text/javascript">
d3.select("body").append("p").text("d3 demo");
</script>
</body>
</html>
```
|
d3 SVG
|
SVG(Scalable Vector Graphics,可缩放矢量图形),等比缩放不失真,兼容多种浏览器。相当于画布,指定 width 和 height 属性。
```html
<svg width="500" height="50">
</svg>
```
像素 px 是默认的度量单位,除了 px 之外,还支持 em、pt、in、cm 和 mm 等其他单位。
```html
<rect x="0" y="0" width="500" height="50"/>
<circle cx="250" cy="25" r="25"/>
<ellipse cx="250" cy="25" rx="100" ry="25"/>
<line x1="0" y1="0" x2="500" y2="50" stroke="black"/>
```
`stroke` 指定直线的颜色。
|
|
SVG 的 text 会继承 CSS 为父元素指定的字体样式,除非另有指定。
```html
<text x="250" y="25" font-family="serif" font-size="25" fill="gray">好好学习</text>
```
要防止任何可见的元素跑到 SVG 画布外面,否则会被裁切掉。
```html
<text x="250" y="50" font-family="serif" font-size="25" fill="gray">天天向上</text>
```
为 SVG 元素添加样式:SVG 的默认样式是黑色填充,没有描边。如果想要其他样式,必须自己给元素添加。
| 属性 | 说明 |
| --- | --- |
| fill | 区域颜色值。与 CSS 用法一样,可以使用颜色名、十六进制值,或 RGB、RGBA 值 |
| stroke | 边的颜色值 |
| stroke-width | 边的宽度,带单位的数值(通常单位是像素) |
| opacity | 0.0(完全透明)到 1.0(完全不透明)之间的数值 |
text 元素还可以使用 `font-family`、`font-size` 属性,用法与 CSS 类似。
```html
<circle cx="25" cy="25" r="22" fill="yellow" stroke="orange" stroke-width="5"/>
<circle cx="25" cy="25" r="22" class="aaa"/>
```
```css
svg .aaa {
/* ... */
}
```
|
|
d3 svg 画面默认在 HTML 层的更上一层,并且相互之间可位置重叠,可设置透明度。
```html
<circle cx="25" cy="25" r="20" fill="rgba(128, 0, 128, 1.0)"/>
<circle cx="50" cy="25" r="20" fill="rgba(0, 0, 255, 0.75)"/>
<circle cx="75" cy="25" r="20" fill="rgba(0, 255, 0, 0.5)"/>
<circle cx="100" cy="25" r="20" fill="rgba(255, 255, 0, 0.25)"/>
<circle cx="125" cy="25" r="20" fill="rgba(255, 0, 0, 0.1)"/>
<circle cx="25" cy="25" r="20" fill="purple" stroke="green" stroke-width="10"
opacity="0.9"/>
<circle cx="65" cy="25" r="20" fill="green" stroke="blue" stroke-width="10"
opacity="0.5"/>
<circle cx="105" cy="25" r="20" fill="yellow" stroke="red" stroke-width="10"
opacity="0.1"/>
```
以第三个圆形为例,其 opacity 值是 0.2(20%),而紫色填充已经设置了 0.75(75%)的透明通道值。结果,紫色区域最终的透明度就是 0.2 × 0.75 = 0.15(15%)。
|
d3 加载数据
|
将数据集绑定到元素列表上,并选出未创建元素的选集,通过 append 添加元素。
```js
let dataset = [ 1,2,3 ];
d3.select("body").selectAll("p")
.data(dataset)
.enter()
.append("p")
.text("day day up");
```
链式调用逐步说明:
- `d3.select("body")`:选择 DOM 中的 body 元素,将其引用交给调用链中的下一个方法
- `.selectAll("p")`:选择 DOM 中的所有段落。因为还没有任何段落,所以返回空选集。可以认为空选集代表接下来会创建的段落
- `.data(dataset)`:遍历解析并输出数据值。dataset 数组中有 n 个值,此后的所有方法都将执行 n 遍,每次针对一个值
- `.enter()`:无数据的占位元素对象选集。如果数据值比对应的 DOM 元素多,就创建一个新的占位元素,然后把这个新占位元素的引用交给链中的下一个方法
- `.append("p")`:取得 enter() 创建的空占位符选集,并把一个 p 元素附加到相应的 DOM 中,然后再把自己刚创建的元素的引用交给链中的下一个方法
- `.text("day day up")`:取得新创建的 p 元素的引用,插入文本值
每一步返回的都是对象的引用,其中从 data 方法开始循环遍历。
通常情况下,DOM 元素个数少于数据集的元素个数,无法完成 DOM 元素与数据集元素的一一绑定,所以 data 绑定数据时,D3 先创建一个空的 DOM 元素对象绑定数据;enter() 可以将空的 DOM 元素选取出来,再通过 append 追加元素,完成 DOM 元素与数据集元素的一一绑定。
|
|
```js
let dataset = [ 1,2,3 ];
d3.select("body").selectAll("p")
.data(dataset)
.enter()
.append("p")
.style("color", function(d) {
if (d > 1) {
return "red";
} else {
return "black";
}
}).text(function(d){
return "day day up "+d;
});
```
`attr()` 和 `style()` 可以分别用来设置选集的 HTML 属性和 CSS 属性;`.classed("bar", true)` 可以让一个 css class 生效/失效。
|
d3 line
|
`stroke` 英 /strəʊk/ 美 /stroʊk/ :n.(打、击等的)一下;击球(动作);一笔。
```js
//Width and height
var w = 600;
var h = 100;
var barPadding = 1;
//Create SVG element
var svg = d3.select("#line_div")
.append("svg")
.attr("width", w)
.attr("height", h);
var dataset = [[30,10,200,50]];
svg.selectAll("line")
.data(dataset)
.enter()
.append("line")
.attr("x1", function(d, i) {
return d[0];
})
.attr("y1", function(d,i) {
return d[1];
})
.attr("x2", function(d, i) {
return d[2];
})
.attr("y2", function(d,i) {
return d[3];
})
.attr("stroke", "black")
.attr("stroke-width", "3");
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text("线")
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return (d[0]+d[2])/2;
})
.attr("y", function(d) {
return d[1]+8;
})
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("fill", "blue");
```
|
d3 rect
```js
//Width and height
var w = 600;
var h = 100;
var barPadding = 1;
//Create SVG element
var svg = d3.select("#rectaa_div")
.append("svg")
.attr("width", w)
.attr("height", h);
var dataset = [[50,30,60,240]];
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return d[0];
})
.attr("y", function(d,i) {
return d[1];
})
.attr("height", function(d, i) {
return d[2];
})
.attr("width", function(d,i) {
return d[3];
}).attr("fill","#efef77")
.attr("stroke", "#778899")
.attr("stroke-width", "3");
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text("矩形")
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return (d[0]+d[2])/2 + 30;
})
.attr("y", function(d) {
return d[1]-10;
})
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("fill", "#357210");
```
d3 bar
随机生成一组 bar。
```css
div.bar {
display: inline-block;
width: 20px;
height: 75px;
margin-right: 2px;
background-color: teal;
}
```
```js
var dataset = []; //Initialize empty array
for (var i = 0; i < 25; i++) { //Loop 25 times
var newNumber = Math.floor(Math.random() * 30); //New random integer (0-29)
dataset.push(newNumber); //Add new number to array
}
d3.select("body").selectAll("div")
.data(dataset)
.enter()
.append("div")
.attr("class", "bar")
.style("height",function (d) {
var barHeight = d * 5;
return barHeight + "px";
});
```
svg绘制条形图+text
```js
//Width and height
var w = 660;
var h = 120;
var barPadding = 8;
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);//等分
})
.attr("y", function(d) {
return h - (d * 4);//左上角为起点
})
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d * 4;//与h-d*4形成一个统一高度h
})
.attr("fill",function (d) {
return "rgb(0, "+Math.round(d * 4)+", " + Math.round(d * 10) + ")";
});
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return i * (w / dataset.length) + (w / dataset.length - barPadding) / 2;
})
.attr("y", function(d) {
return h - (d * 4) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
```
d3 circle
```js
//Width and height
var w = 500;
var h = 150;
//Data
var dataset = [ 5, 10, 15, 20, 25 ];
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var circles = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle");
circles.attr("cx", function(d, i) {
return (i * 50) + 25;
})
.attr("cy", h/2)
.attr("r", function(d) {
return d;
})
.attr("fill", "yellow")
.attr("stroke", "orange")
.attr("stroke-width", function(d) {
return d/2;
});
```
```js
// 宽度和高度
var width = 700;
var height = 300;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
let dataset = [ 5, 10, 15, 20, 25 ];
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d, i) {
return (i * 50) + 25;
})
.attr("cy", height/2)
.attr("r", function(d) {
return d;
})
.attr("fill","pink")
.attr("stroke", "orange")
.attr("stroke-width", function(d) {
return d/2;
});
```
d3 scatterplot
散点的半径大小体现数值的大小
```js
var w = 600;
var h = 100;
var dataset = [
[5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
];
//Create SVG element
var svg = d3.select("#d307_scatterplot")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return d[0];
})
.attr("cy", function(d) {
return d[1];
})
.attr("r", function(d) {
return Math.sqrt(h - d[1]);
})
.attr("fill","#999");
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d[0] + "," + d[1];
})
.attr("x", function(d) {
return d[0];
})
.attr("y", function(d) {
return d[1];
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "blue");
```
d3 scale
scale:比例尺,同比/等比映射,将 `domain([100, 300])` 同比映射到 `range([10, 30])`。
```js
let scale = d3.scaleLinear()
.domain([100, 300])
.range([10, 30]);
console.log(scale(200));//20
```
`d3.max`:求数据集中的最大值,可传访问器函数。
```js
let simpleDataset = [7, 8, 4, 5, 2];
d3.max(simpleDataset); // 返回8
let dataset = [
[5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
];
d3.max(dataset, function(d) {
return d[0];
});
```
动态同比缩放:将svg画布映射到 [padding, w - padding * 2] 的范围内,但相对大小/位置 不变
```js
//Width and height
var w = 600;
var h = 300;
var padding = 30;
var dataset = [
[5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88],
[500, 150]
];
//Create scale functions
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[0]; })])
.range([padding, w - padding * 2]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([h - padding, padding]);
var rScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([2, 5]);
//Create SVG element
var svg = d3.select("#d3scatterplotdt")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create circles
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d[0]);
})
.attr("cy", function(d) {
return yScale(d[1]);
})
.attr("r", function(d) {
return rScale(d[1]);
})
.attr("fill","#999");
//Create labels
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d[0] + "," + d[1];
})
.attr("x", function(d) {
return xScale(d[0]);
})
.attr("y", function(d) {
return yScale(d[1]);
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "red");
```
比例尺举例——平方根比例尺:
```js
var aScale = d3.scaleSqrt()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([0, 10]);
```
时间比例尺:
```js
//Width and height
var w = 500;
var h = 300;
var padding = 40;
var dataset, xScale, yScale; //Empty, for now
//For converting strings to Dates
// 01/01/17格式的时间
var parseTime = d3.timeParse("%m/%d/%y");
//For converting Dates to strings
var formatTime = d3.timeFormat("%b %e");
dataset = [];
d3.csv("static/data/time_scale_data.csv",function(d,i){
var di = {
Date: parseTime(d.Date),
Amount: parseInt(d.Amount)
}
dataset.push(di);
});
setTimeout(function() {
console.log(dataset);
aa();
}, 2000);
function aa(){
//Create scale functions
xScale = d3.scaleTime()
.domain([
d3.min(dataset, function(d) { return d.Date; }),
d3.max(dataset, function(d) { return d.Date; })
])
.range([padding, w - padding]);
yScale = d3.scaleLinear()
.domain([
d3.min(dataset, function(d) { return d.Amount; }),
d3.max(dataset, function(d) { return d.Amount; })
])
.range([h - padding, padding]);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Generate date labels first, so they are in back
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return formatTime(d.Date);
})
.attr("x", function(d) {
return xScale(d.Date) + 4;
})
.attr("y", function(d) {
return yScale(d.Amount) + 4;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "#bbb");
//Generate circles last, so they appear in front
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d.Date);
})
.attr("cy", function(d) {
return yScale(d.Amount);
})
.attr("r", 2);
}
```
d3 数轴
本节摘要列表
d3 时间数轴
d3 数轴优化:增加竖线
定义刻度尺:
```js
d3.axisLeft和d3.axisRight可以生成垂直数轴,
d3.axisTop和d3.axisBottom可以生成水平数轴,
Left,Right,Top,Bottom指的是数轴上刻度标,就是个那个小竖条的方向,
而整个数轴顶点的默认坐标都是[0,0]
//axisBottom,水平方向,起点为[0,0]的一段线段
var xAxis = d3.axisBottom()
.scale(xScale);
等效于
var xAxis = d3.axisBottom(xScale);
```
调用刻度尺:创建一个层叫 `g` 用于分组;坐标轴绘制需要放到后面,即先绘制其他图形,然后再绘制坐标轴。
```js
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
```
优化刻度尺:
```js
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5); // 粗略地设置刻度线的数量
//刻度数字格式化
var formatAsPercentage = d3.format(".1%");
xAxis.tickFormat(formatAsPercentage);
```
```js
// 宽度和高度
//Width and height
var w = 600;
var h = 200;
var padding = 30;
var dataset = [
[5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88],
[600, 150]
];
var formatAsPercentage = d3.format("1");
//Create scale functions
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[0]; })])
.range([padding, w - padding * 2]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([h - padding, padding]);
//Define X axis
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5).tickFormat(formatAsPercentage);
//这里D3根据值的分布会进行区间的调整,tick是建议,不是绝对
var yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5).tickFormat(formatAsPercentage);
//Create SVG element
var svg = d3.select("#svg_keduchi111")
.append("svg")
.attr("width", w)
.attr("height", h);
var aScale = d3.scaleSqrt()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([0, 10]);
//Create circles
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d[0]);
})
.attr("cy", function(d) {
return yScale(d[1]);
})
.attr("r", function(d) {
return aScale(d[1]);
}).attr("fill","pink");
//Create labels
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d[0] + "," + d[1];
})
.attr("x", function(d) {
return xScale(d[0]);
})
.attr("y", function(d) {
return yScale(d[1]);
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "blue");
//Create X axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + (padding) + ",0)")
.call(yAxis);
```
随机数轴:
```js
//Width and height
var w = 500;
var h = 300;
var padding = 30;
/*
//Static dataset
var dataset = [
[5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88],
[600, 150]
];
*/
//Dynamic, random dataset
var dataset = []; //Initialize empty array
var numDataPoints = 12; //Number of dummy data points to create
var xRange = Math.random() * 1000; //Max range of new x values
var yRange = Math.random() * 1000; //Max range of new y values
for (var i = 0; i < numDataPoints; i++) { //Loop numDataPoints times
var newNumber1 = Math.floor(Math.random() * xRange); //New random integer
var newNumber2 = Math.floor(Math.random() * yRange); //New random integer
dataset.push([newNumber1, newNumber2]); //Add new number to array
}
//Create scale functions
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[0]; })])
.range([padding, w - padding * 2]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([h - padding, padding]);
var aScale = d3.scaleSqrt()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([0, 10]);
//Define X axis
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5);
//Define Y axis
var yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create circles
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d[0]);
})
.attr("cy", function(d) {
return yScale(d[1]);
})
.attr("r", function(d) {
return aScale(d[1]);
})
.attr("fill","#abc");
//Create labels
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d[0] + "," + d[1];
})
.attr("x", function(d) {
return xScale(d[0]);
})
.attr("y", function(d) {
return yScale(d[1]);
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "#889");
//Create X axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
//Create Y axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
```
时间数轴
```js
//Width and height
var w = 500;
var h = 300;
var padding = 40;
var dataset, xScale, yScale, xAxis, yAxis; //Empty, for now
//For converting strings to Dates
var parseTime = d3.timeParse("%Y-%m-%d");
//For converting Dates to strings
var formatTime = d3.timeFormat("%b %e");
dataset = [];
let a1 = [{"Date":"2016-12-31","Amount":35},{"Date":"2017-01-01","Amount":30},{"Date":"2017-01-02","Amount":24},{"Date":"2017-01-03","Amount":37},{"Date":"2017-01-04","Amount":54},{"Date":"2017-01-05","Amount":55},{"Date":"2017-01-06","Amount":62},{"Date":"2017-01-07","Amount":62},{"Date":"2017-01-08","Amount":70},{"Date":"2017-01-09","Amount":66},{"Date":"2017-01-10","Amount":51},{"Date":"2017-01-11","Amount":63},{"Date":"2017-01-12","Amount":74},{"Date":"2017-01-13","Amount":58},{"Date":"2017-01-14","Amount":69},{"Date":"2017-01-15","Amount":56},{"Date":"2017-01-16","Amount":56},{"Date":"2017-01-17","Amount":50},{"Date":"2017-01-18","Amount":52},{"Date":"2017-01-19","Amount":48},{"Date":"2017-01-20","Amount":55},{"Date":"2017-01-21","Amount":44},{"Date":"2017-01-22","Amount":35},{"Date":"2017-01-23","Amount":32},{"Date":"2017-01-24","Amount":35},{"Date":"2017-01-25","Amount":21},{"Date":"2017-01-26","Amount":15},{"Date":"2017-01-27","Amount":32},{"Date":"2017-01-28","Amount":21},{"Date":"2017-01-29","Amount":12},{"Date":"2017-01-30","Amount":23}];
dataset = a1.map(function(d,i){
return {
Date: parseTime(d.Date),
Amount: parseInt(d.Amount)
};
});
function aa(){
//Create scale functions
xScale = d3.scaleTime()
.domain([
d3.min(dataset, function(d) { return d.Date; }),
d3.max(dataset, function(d) { return d.Date; })
])
.range([padding, w - padding]);
yScale = d3.scaleLinear()
.domain([
d3.min(dataset, function(d) { return d.Amount; }),
d3.max(dataset, function(d) { return d.Amount; })
])
.range([h - padding, padding]);
//Create SVG element
var svg = d3.select("#svg_timsaxi")
.append("svg")
.attr("width", w)
.attr("height", h);
//Generate date labels first, so they are in back
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return formatTime(d.Date);
})
.attr("x", function(d) {
return xScale(d.Date) + 4;
})
.attr("y", function(d) {
return yScale(d.Amount) + 4;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "#bbb");
//Generate circles last, so they appear in front
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d.Date);
})
.attr("cy", function(d) {
return yScale(d.Amount);
})
.attr("r", 2);
//Define X axis
xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5);
//Define Y axis
yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5);
//Create X axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
//Create Y axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
}
aa();
```
时间数轴优化:增加竖线
```js
//Width and height
var w = 700;
var h = 300;
var padding = 60;
var dataset, xScale, yScale, xAxis, yAxis; //Empty, for now
//For converting strings to Dates
var parseTime = d3.timeParse("%Y-%m-%d");
//For converting Dates to strings
// var formatTime = d3.timeFormat("%b %e");
//For converting Dates to strings
var formatTime = d3.timeFormat("%e");
dataset = [];
a1 = [{"Date":"2016-12-31","Amount":35},{"Date":"2017-01-01","Amount":30},{"Date":"2017-01-02","Amount":24},{"Date":"2017-01-03","Amount":37},{"Date":"2017-01-04","Amount":54},{"Date":"2017-01-05","Amount":55},{"Date":"2017-01-06","Amount":62},{"Date":"2017-01-07","Amount":62},{"Date":"2017-01-08","Amount":70},{"Date":"2017-01-09","Amount":66},{"Date":"2017-01-10","Amount":51},{"Date":"2017-01-11","Amount":63},{"Date":"2017-01-12","Amount":74},{"Date":"2017-01-13","Amount":58},{"Date":"2017-01-14","Amount":69},{"Date":"2017-01-15","Amount":56},{"Date":"2017-01-16","Amount":56},{"Date":"2017-01-17","Amount":50},{"Date":"2017-01-18","Amount":52},{"Date":"2017-01-19","Amount":48},{"Date":"2017-01-20","Amount":55},{"Date":"2017-01-21","Amount":44},{"Date":"2017-01-22","Amount":35},{"Date":"2017-01-23","Amount":32},{"Date":"2017-01-24","Amount":35},{"Date":"2017-01-25","Amount":21},{"Date":"2017-01-26","Amount":15},{"Date":"2017-01-27","Amount":32},{"Date":"2017-01-28","Amount":21},{"Date":"2017-01-29","Amount":12},{"Date":"2017-01-30","Amount":23}];
dataset = a1.map(function(d,i){
return {
Date: parseTime(d.Date),
Amount: parseInt(d.Amount)
};
})
function aa2(){
//Create scale functions
xScale = d3.scaleTime()
.domain([
d3.min(dataset, function(d) { return d.Date; }),
d3.max(dataset, function(d) { return d.Date; })
])
.range([padding, w - padding]);
yScale = d3.scaleLinear()
.domain([
d3.min(dataset, function(d) { return d.Amount; }),
d3.max(dataset, function(d) { return d.Amount; })
])
.range([h - padding, padding]);
//Create SVG element
var svg = d3.select("#svg_ketimyh111")
.append("svg")
.attr("width", w)
.attr("height", h);
//Generate date labels first, so they are in back
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return formatTime(d.Date);
})
.attr("x", function(d) {
return xScale(d.Date) + 4;
})
.attr("y", function(d) {
return yScale(d.Amount) + 4;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "#bbb");
//Generate guide lines
svg.selectAll("line")
.data(dataset)
.enter()
.append("line")
.attr("x1", function(d) {
return xScale(d.Date);
})
.attr("x2", function(d) {
return xScale(d.Date);
})
.attr("y1", h - padding)
.attr("y2", function(d) {
return yScale(d.Amount);
})
.attr("stroke", "#ddd")
.attr("stroke-width", 1);
//Generate circles last, so they appear in front
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d.Date);
})
.attr("cy", function(d) {
return yScale(d.Amount);
})
.attr("r", 2).attr("fill","blue");
//Define X axis
xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5).tickFormat(formatTime);
//Define Y axis
yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5);
//Create X axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
//Create Y axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
}
aa2();
```
d3 更新过渡及动画
简单条形图回顾
使用分档比例尺
简单条形图回顾:没有使用比例尺,使用数据本身做简单处理
```js
//Width and height
var w = 600;
var h = 200;
var barPadding = 1;
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - (d * 4);
})
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d * 4;
})
.attr("fill", function(d) {
return "rgb(0, 0, " + Math.round(d * 10) + ")";
});
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return i * (w / dataset.length) + (w / dataset.length - barPadding) / 2;
})
.attr("y", function(d) {
return h - (d * 4) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
```
使用分档比例尺:d3.scaleBand()
点击这一行文本使用新数据更新条形图.
```js
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, w])
.paddingInner(0.05);
```
- `d3.scaleBand()` 是离散范围值;`d3.range(dataset.length)` 即 `d3.range(20)`,数组长度 20 分为 20 档,w=600 时一档宽度 30
- `paddingInner(0.05)`:30×0.05=1.5 的 inner padding 距离
- 1.5 宽度导致 w 的宽度出现小数,需要对半个像素值进行抗锯齿处理,否则条形会显得模糊。`rangeRound` 可以让分档比例尺把输出值四舍五入成最接近的整数,整数值有助于将视觉元素与像素网格对齐,保证图形的边缘清晰锐利
添加点击事件:
```js
d3.select("p")
.on("click", function() {
// 更新数据,然后重新绘制图形
});
```
动画 transition:
```js
.. // 选择元素的代码
.transition()
.duration(2000)
.ease(d3.easeLinear)
... // attr()的代码
```
- `transition()` 添加动画效果
- `duration(time_sec)` 指定这个动画持续的时间,单位毫秒
- `ease(...)` 指定动画转变的具体细节:
- `d3.easeCubicOut`:逐渐加速然后再逐渐减速的效果
- `d3.easeLinear`:线性缓动,动画速度不变
- `d3.easeCircleIn`:元素逐渐进入并加速,最后到达指定位置
- `d3.easeElasticOut`:描述这个效果最恰当的词是"有弹性"
- `d3.easeBounceOut`:像皮球落地一样反复弹跳,慢慢停下来
延时时间:`ease()` 负责控制动画性质,`delay()` 用于指定过渡开始的时间。
```js
...
.transition()
.delay(1000) // 1000毫秒,即1秒
.duration(2000) // 2000毫秒,即2秒
...
```
完整代码:
```js
//Width and height
var w = 600;
var h = 250;
var padding = 50;
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, w])
.paddingInner(0.05);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, h - padding]);
//Create SVG element
var svg = d3.select("#fendangcs_div")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create bars
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.bandwidth())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + Math.round(d * 10) + ")";
});
//Create labels
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.bandwidth() / 2;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
d3.select("#fendangcsp_click")
.on("click",function () {
//New values for dataset
var numValues = dataset.length; //Count original length of dataset
var maxValue = 100;
dataset = []; //Initialize empty array
for (var i = 0; i < numValues; i++) { //Loop numValues times
var newNumber = Math.floor(Math.random() * maxValue); //New random integer (0-24)
dataset.push(newNumber); //Add new number to array
}
yScale.domain([0,d3.max(dataset)]);
d3.select("#fendangcs_div").selectAll("rect")
.data(dataset)
.transition()
.delay(function (d,i) {
return i * 100;
})
.duration(500)
.ease(d3.easeLinear)
.attr("y",function (d) {
return h - padding - yScale(d);
})
.attr("height",function (d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + Math.round(d * 10) + ")";
});
//Update all labels
svg.selectAll("text")
.data(dataset)
.transition()
.delay(function (d,i) {
return i * 100;
})
.duration(500)
.ease(d3.easeLinear)
.text(function(d) {
return d;
})
.attr("x", function(d, i) {
return xScale(i) + xScale.bandwidth() / 2;
})
.attr("y", function(d) {
return h - padding - yScale(d) + 14;
});
});
```
参考
D3 官网