- A+
D3
D3 or D3.js 代表 "Data Driven Documents"
选中、添加元素
select()
方法从文档中选择一个元素,它接收目标元素的名称作为参数并返回第一个匹配该名称 HTML 节点。举例:
const anchor = d3.select('a');
append()
方法接收添加到文档中的元素,它会把该元素添加到一个选中的 HTML 节点,然后返回对该节点的引用。
text()
方法可以设置被选中节点的文本也可以得到当前文本。若是设置文本,需要将字符串作为参数传递。
D3 允许方法的嵌套。
下面是一个选中无序列表,并添加一个 list 元素的方法:
d3.select("ul") .append("li") .text("very important")
选中一组元素
使用 selectAll()
选中一组元素。它返回一个 HTML 节点数组。
下面是一个选中所有超链接元素的例子:
const anchors = d3.selectAll("a");
例子:
d3.selectAll("li") .text("list item")
使用数据
首先使用 data()
方法去选择 DOM 元素来和数据联系在一起。数据集作为参数传给该方法。
使用 enter()
方法为数据集中每一块元素创建一个新的 DOM 元素。
下面的例子是选择 ul 元素并根据数组创建列表。
const dataset = ["a", "b", "c"]; d3.select("ul").selectAll("li") .data(dataset) .enter() .append("li") .text("New Item");
使用动态数据
text()
方法可以接收字符串或者回调函数作为参数。
selection.text((d) => d)
在上面的这个方法中,参数 d
指的是该数据集的一个入口。
<body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; d3.select("body").selectAll("h2") .data(dataset) .enter() .append("h2") // Add your code below this line .text((d) => d + " USD"); // Add your code above this line </script> </body> /* 12 USD 31 USD 22 USD 17 USD 25 USD 18 USD 29 USD 14 USD 9 USD */
给元素加内联样式
使用 style()
各动态元素添加样式。该方法接收一个以逗号 ,
分隔的键值对作为参数。
下面是一个把选中文本设置为蓝色的例子:
selection.style("color","blue");
设置选中字体:
d3.select("body") .style("font-family","verdana");
基于数据改动样式
在 style()
中同样可以使用回调函数来改动不同元素的样式。使用参数 d
来代表数据点。
下面的例子是把数据集中小于 20 的元素设置为红色,其它设置为绿色。
d3.selectAll("h2") .style("color", (d) => { if (d < 20) { return "red"; } else return "green"; });
添加 class 属性
attr()
方法和 style()
方法类似,他接收以逗号分割的值,并且可以使用回调函数。
下面是一个给元素添加 container class 的例子。
selection.attr("class", "container");
动态修改元素的高度
结合上面所学创建一个条形图 (bar chart)。
<style> .bar { width: 25px; height: 100px; display: inline-block; background-color: blue; } </style> <body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; d3.select("body").selectAll("div") .data(dataset) .enter() .append("div") .attr("class", "bar") .style("height", (d) => d + "px"); </script> </body>
改变条形图的展示
<style> .bar { width: 25px; height: 100px; /* Add your code below this line */ marign: 2px; /* Add your code above this line */ display: inline-block; background-color: blue; } </style> <body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; d3.select("body").selectAll("div") .data(dataset) .enter() .append("div") .attr("class", "bar") .style("height", (d) => (d*10 + "px")) // Change this line </script> </body>
SVG in d3
SVG 的全称是:Scalable Vector Graphics。
scalable 的意思是对物体放大或者缩小不会使物体马赛克(pixelated)。
SVG 在 HTML 中使用 svg
标签实现。
下面是一个创建 SVG 的例子:
<style> svg { background-color: pink; } </style> <body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; const w = 500; const h = 1 00; const svg = d3.select("body") // Add your code below this line svg.append("svg").attr("width",w).attr("height",h); // Add your code above this line </script> </body>
SVG 形状
SVG 支持多种数量的形状,例如:使用 <rect>
来表示矩形。
当把一个形状放入 SVG 区域时,可以设置 x,y 坐标系。原点(0,0)表示左上角。x 的正值会使图形向右移动,y 的正值向上移动。例如:把一个形状放在 500 宽,100 高的位置需要设置 x = 250,y = 50。
对于 <rect>
来说,它有四个属性,分别是高度、宽度和 x,y。该元素必须添加到 svg节点,而不是 body 节点。
下面的例子是设置一个矩形
<body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; const w = 500; const h = 100; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h) // Add your code below this line svg.append("rect") .attr("width", 25) .attr("height", 100) .attr("x", 0) .attr("y", 0); // Add your code above this line </script> </body>
对数据集中的每一个数据点创建一个 bar
<body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; const w = 500; const h = 100; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") // Add your code below this line .data(dataset) .enter() .append("rect") // Add your code above this line .attr("x", 0) .attr("y", 0) .attr("width", 25) .attr("height", 100); </script> </body>
对每个 bar 动态设置坐标
条形图的 y 坐标应该是一致的,而 x 坐标应该有所不同。通过设置 attr()
的回调函数来动态设置。回调函数接收两个参数,第一个用 d 代表数据点本身,第二个代表数据点在数组中的索引。格式:
selection.attr("property", (d,i) => { })
注:不需要使用 for 或者 forEach() 循环。
下面是扩大 30 倍的例子:
<body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; const w = 500; const h = 100; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", (d, i) => { // Add your code below this line return i*30; // Add your code above this line }) .attr("y", 0) .attr("width", 25) .attr("height", 100); </script> </body>
动态改变高度
实际上就是修改数据点
<body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; const w = 500; const h = 100; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", (d, i) => i * 30) .attr("y", 0) .attr("width", 25) .attr("height", (d, i) => { // Add your code below this line return d * 3; // Add your code above this line }); </script> </body>
倒置 SVG 元素
y
坐标也就是 y = heightOfSVG - heightOfBar
会把 bar 置于右上侧。
一般公式:y = h - m * d
,其中 m 是数据点 scale 的常量。
<body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; const w = 500; const h = 100; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", (d, i) => i * 30) .attr("y", (d, i) => { // Add your code below this line return 100 - 3 * d; // Add your code above this line }) .attr("width", 25) .attr("height", (d, i) => 3 * d); </script> </body>
改变 SVG 图片的颜色
在 SVG 中,rect
形状使用 fill
来控制颜色。
下面设置颜色为海军蓝。
<body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; const w = 500; const h = 100; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", (d, i) => i * 30) .attr("y", (d, i) => h - 3 * d) .attr("width", 25) .attr("height", (d, i) => 3 * d) // Add your code below this line .attr("fill", "navy"); // Add your code above this line </script> </body>
加标签
使用 SVG 的 text
给元素加标签。该属性同样具有 x 和 y 属性。
<body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; const w = 500; const h = 100; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", (d, i) => i * 30) .attr("y", (d, i) => h - 3 * d) .attr("width", 25) .attr("height", (d, i) => 3 * d) .attr("fill", "navy"); svg.selectAll("text") .data(dataset) .enter() // Add your code below this line .append("text") .attr("x", (d, i) => i * 30) .attr("y", (d, i) => h - 3 * d - 3) .text((d, i) => d); // Add your code above this line </script> <body>
给标签加样式
<body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; const w = 500; const h = 100; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", (d, i) => i * 30) .attr("y", (d, i) => h - 3 * d) .attr("width", 25) .attr("height", (d, i) => d * 3) .attr("fill", "navy"); svg.selectAll("text") .data(dataset) .enter() .append("text") .text((d) => d) .attr("x", (d, i) => i * 30) .attr("y", (d, i) => h - (3 * d) - 3) // Add your code below this line .attr("fill","red") .style("font-size", "25px") // Add your code above this line </script> </body>
给元素添加 hover 效果
<style> .bar:hover { fill: brown; } </style> <body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; const w = 500; const h = 100; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", (d, i) => i * 30) .attr("y", (d, i) => h - 3 * d) .attr("width", 25) .attr("height", (d, i) => 3 * d) .attr("fill", "navy") // Add your code below this line .attr("class", "bar") // Add your code above this line svg.selectAll("text") .data(dataset) .enter() .append("text") .text((d) => d) .attr("x", (d, i) => i * 30) .attr("y", (d, i) => h - (3 * d) - 3); </script> </body>
给元素添加 tooltip
当用户 hover 在一个元素上,tooltip 可以展示更多的信息。
使用 SVG 的 title
给元素添加 tooltip。使用 title
配合 text()
方法给条动态添加数据。
下面是一个例子:
<style> .bar:hover { fill: brown; } </style> <body> <script> const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]; const w = 500; const h = 100; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", (d, i) => i * 30) .attr("y", (d, i) => h - 3 * d) .attr("width", 25) .attr("height", (d, i) => d * 3) .attr("fill", "navy") .attr("class", "bar") // Add your code below this line .append("title") .text((d) => { return d; }) // Add your code above this line svg.selectAll("text") .data(dataset) .enter() .append("text") .text((d) => d) .attr("x", (d, i) => i * 30) .attr("y", (d, i) => h - (d * 3 + 3)) </script> </body>
使用 SVG 环形创建 scatterplot
scatterplot 是另一种可视化的类型。它使用圆环来遍历数据点,每个数据点有两个值。这些值和 x、y 坐标绑定,用来定位圆环的位置。
SVG 的 circle
标签创建圆形。
下面是一个圆形的例子:
<body> <script> const dataset = [ [ 34, 78 ], [ 109, 280 ], [ 310, 120 ], [ 79, 411 ], [ 420, 220 ], [ 233, 145 ], [ 333, 96 ], [ 222, 333 ], [ 78, 320 ], [ 21, 123 ] ]; const w = 500; const h = 500; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("circle") // Add your code below this line .data(dataset) .enter() .append("circle"); // Add your code above this line </script> </body>
给圆形添加属性
圆形主要有三个属性。cx
和 cy
属性是坐标系。r
属性规定圆形的半径。
这三个属性可以使用一个回调函数动态设置它们的值。记住,所有在 data(dataset)
链后的方法会对每个数据项运行一次。回调函数中的 d
表示当前数据集合中的数据项。可以使用括号表示法去得到数据集中的元素,如:d[0]
。
下面是一个例子:
<body> <script> const dataset = [ [ 34, 78 ], [ 109, 280 ], [ 310, 120 ], [ 79, 411 ], [ 420, 220 ], [ 233, 145 ], [ 333, 96 ], [ 222, 333 ], [ 78, 320 ], [ 21, 123 ] ]; const w = 500; const h = 500; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("circle") .data(dataset) .enter() .append("circle") // Add your code below this line .attr("cx", (d) => d[0]) .attr("cy", (d) => h - d[1]) .attr("r", 5) // Add your code above this line </script> </body>
给 scatterplot 添加标签
<body> <script> const dataset = [ [ 34, 78 ], [ 109, 280 ], [ 310, 120 ], [ 79, 411 ], [ 420, 220 ], [ 233, 145 ], [ 333, 96 ], [ 222, 333 ], [ 78, 320 ], [ 21, 123 ] ]; const w = 500; const h = 500; const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", (d, i) => d[0]) .attr("cy", (d, i) => h - d[1]) .attr("r", 5); svg.selectAll("text") .data(dataset) .enter() .append("text") // Add your code below this line .attr("x", (d) => d[0] + 5) .attr("y", (d) => h - d[1]) .text((d) => (d[0] + ", " + d[1])) // Add your code above this line </script> </body>
创建线性 scale
scales
是一种函数,可以把数据集的数据映射为 SVG 画布。
例子:
const scale = d3.scaleLinear();
默认情况,scale 使用实体关系。输入和输出的结果相同。
<body> <script> // Add your code below this line const scale = d3.scaleLinear(); // Create the scale here const output = scale(50); // Call scale with an argument here // Add your code above this line d3.select("body") .append("h2") .text(output); </script> </body>
给 scale 设置 domain 和 range
假如一个数据集范围从50 - 480,这是输入的范围,也叫 domain。
然后打算把这些数据点映射到 x 轴上,从 10 units 到 500 units,这就是输出的范围,也叫 range。
domian()
和 range()
方法为 scale 设置这些值。这两个方法接收一个至少包含两个元素数组作为参数。例如:
scale.domain([50, 480]); scale.range([50, 480]); scale(40); d3.scaleLinear();
最小的 domain 是 50 映射到最小的 range 为 10。
例子:
<body> <script> // Add your code below this line const scale = d3.scaleLinear(); scale.domain([250, 500]).range([10, 150]); // Add your code above this line const output = scale(50); d3.select("body") .append("h2") .text(output); </script> </body>
使用 d3.max 和 d3.min 方法找出数据集的最大和最小元素
例子:
const exampleData = [3, 45, 10, 9]; d3.min(exampleData); d3.max(exampleData);
有些时候数据集是嵌套的,这样的情况下,需要给这两个函数传入回调函数。这时,参数 d
代表当前内部的数组。
const locationData = [[1, 7],[6, 3],[8, 3]]; const minX = d3.min(locationData, (d) => d[0]); // minX = 1; <body> <script> const positionData = [[1, 7, -4],[6, 3, 8],[2, 9, 3]] // Add your code below this line const output = d3.max(positionData, (d) => d[2]); // Change this line // Add your code above this line d3.select("body") .append("h2") .text(output) </script> </body> // output = 8;
使用动态 scale
<body> <script> const dataset = [ [ 34, 78 ], [ 109, 280 ], [ 310, 120 ], [ 79, 411 ], [ 420, 220 ], [ 233, 145 ], [ 333, 96 ], [ 222, 333 ], [ 78, 320 ], [ 21, 123 ] ]; const w = 500; const h = 500; // Padding between the SVG canvas boundary and the plot const padding = 30; // Create an x and y scale const xScale = d3.scaleLinear() .domain([0, d3.max(dataset, (d) => d[0])]) .range([padding, w - padding]); // Add your code below this line const yScale = d3.scaleLinear() .domain([0, d3.max(dataset, (d) => d[1])]) .range([h - padding, padding]); // Add your code above this line const output = yScale(411); // Returns 30 d3.select("body") .append("h2") .text(output) </script> </body>
使用预定义的 scale 放置元素
<body> <script> const dataset = [ [ 34, 78 ], [ 109, 280 ], [ 310, 120 ], [ 79, 411 ], [ 420, 220 ], [ 233, 145 ], [ 333, 96 ], [ 222, 333 ], [ 78, 320 ], [ 21, 123 ] ]; const w = 500; const h = 500; const padding = 60; const xScale = d3.scaleLinear() .domain([0, d3.max(dataset, (d) => d[0])]) .range([padding, w - padding]); const yScale = d3.scaleLinear() .domain([0, d3.max(dataset, (d) => d[1])]) .range([h - padding, padding]); const svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", (d) => xScale(d[0])) .attr("cy", (d) => yScale(d[1])) .attr("r", 5); svg.selectAll("text") .data(dataset) .enter() .append("text") .text((d) => (d[0] + ", " + d[1])) .attr("x", (d) => xScale(d[0] + 10)) .attr("y", (d) => yScale(d[1])); </script> </body>
加入 Axes
axisLeft()
和 axisBottom()
方法,去渲染 x 轴和 y 轴,相对的,根据 xScale 创建 xAxis
const xAxis = d3.axisBottom(xScale);
下一步就是渲染
svg.append("g") .attr("transform", "translate(0, " + (h - padding) + ")") .call(xAxis);