七、可视化数据
能够可视化数据使其更容易理解。它允许您看到原本可能隐藏的模式,并讲述有关您的数据的故事。您可以用纯 JavaScript 创建数据可视化,但是有许多 JavaScript 库可以使这变得更容易。您将在本书中使用的库是 D3.js。在本章中,您将使用 D3.js 创建一个条形图,其数据收集方式与第 6 章相同。在本章的最后,您将看到一个条形图,其中包含来自一个电位计的数据。
D3.js 简介
D3 代表数据驱动文档;这是一个数据可视化 JavaScript 库。它允许您将数据绑定到文档对象模型(DOM ),以便用 DOM 元素在网页上显示可视化效果。这些可以是任何 DOM 元素:例如,text 或 div。在本书的例子中,SVG DOM 元素将用于显示数据。
D3.js 有许多函数可以构建不同类型的数据可视化。这些包括条形图,折线图,choropleth 图,气泡图,等等。它通过数学运算将数据转化为可视化数据。
它允许您使用许多不同的数据文件类型,包括 JSON、GeoJSON 和 CSV。
您可以使用其他库在 JavaScript 中创建数据可视化;我在附录 b 中列出了其中的一些。
D3.js 如何工作
使用 D3.js,首先选择一个 HTML 元素来保存整个可视化,然后决定要使用哪种类型的 DOM 元素来保存每个数据:例如,text、div 或 SVG。接下来,将数据绑定到这些元素,然后添加显示数据的 SVG 或 DOM 元素。然后,您可以对形状进行定位、缩放和着色。
D3.js 会帮你计算。如果你有一个 2、5 和 9 的数据集,你想做三个条形图,你知道这是你最小的数字,9 是最大的。你可以创建一个从 0 到 9 的图表。但是如果数据被更新,从-4 变成 22,或者引入第四个数字,会发生什么呢?轴线会是错的。当处理实时数据时,你需要一个可以随数据伸缩的轴,D3.js 有这样的功能。
数据值是输入域,经过缩放后成为输出范围。输入域的最低和最高输入数字被映射到输出范围的最低和最高数字。图 7-1 显示输入域,0 到 55 的输入数据,0 到 500 的输出范围。
图 7-1
The input domain and output range
D3.js 对数据使用一般的更新模式:进入、更新和退出。Enter 用于新数据,它添加一个 DOM 元素,当数据改变时更新,不添加或删除任何 DOM 元素,当 DOM 元素数量减少时退出。当数据更新时,如果 DOM 元素的数量需要增加,或者如果 DOM 元素的数量需要减少,使用这种模式允许 DOM 元素的动画转换。该模式的功能如图 7-2 所示。
图 7-2
The functions for the general update pattern
如果存在没有 DOM 元素的数据,则使用函数 enter()。当有一个不再有数据的 DOM 元素时,使用 exit()。当 DOM 元素的数量与数据的数量相同,但数据发生了变化时,使用 update()函数。图 7-3 显示了这些功能是如何工作的。
图 7-3
How elements are added, updated, and removed from the DOM with D3.js Try D3.JS
理解 D3.js 最好的方法就是开始使用它。在将 D3.js 与 Arduino 数据一起使用之前,最好先通过构建一个条形图来了解一下基础知识。清单 7-1 中的代码将使用一些随机数据制作一个条形图。该数据将定义条的高度、轴的标签和轴的刻度。id 为“viz”的 div 将包含可视化效果。打开文本编辑器,复制清单 7-1 中的代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3.js</title>
<script type="text/javascript" src="https://d3js.org/d3.v4.js"></script>
<style type="text/css">
body{
font-family: arial;
}
h1{
font-size: 22px;
margin: 0px;
}
h2{
font-size: 16px;
margin: 0px;
margin-top: 2px;
}
.axis text {
font-family: arial;
font-size: 12px;
font-weight: normal;
color: pink;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: #fd8f00;
}
h1, h2, p{
margin-left: 40px;
}
p{
font-size: 12px;
}
</style>
</head>
<body>
<h1>A D3 bar chart</h1>
<div id = "viz"></div>
<script type="text/javascript">
var margin = {top: 20, right: 20, bottom: 40, left: 40};
var width = 480 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var data = [
{"amount": 5, "name": "column1"},
{"amount": 11, "name": "column2"},
{"amount": 55, "name": "column3"},
{"amount": 23, "name": "column4"}
]
var x = d3.scaleBand()
.domain(data.map(function(d) { return d.name; }))
.range([0, width], .1)
.padding(0.1);
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return +d.amount; })])
.range([height, 0]);
var dataviz = d3.select("#viz").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
dataviz.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.name); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(+d.amount); })
.attr("height", function(d) { return height - y(+d.amount); });
dataviz.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
dataviz.append("g")
.call(d3.axisLeft(y));
</script>
</body>
</html>
Listing 7-1bar_chart.html
打开浏览器,打开 bar_chart.html 文件;您将看到数据可视化。
代码解释
表 7-1
bar_chart.html explained
| <script type="text/javascript" src="
https://d3js.org/d3.v4.js
">
| 您可以从 URL 下载 D3.js 或将其包含在您的页面中。 |
| <style type="text/css"></style>
| 由于 D3.js 将数据附加到 DOM 元素上,所以这些元素可以用 CSS 进行样式化。通常你会创建一个单独的 CSS 文件,但是在这个例子中 CSS 是在 HTML 页面上。 |
| var margin = {top: 20, right: 20, bottom: 40, left: 40};
| 如果可视化与 SVG 视口的大小相同,就没有空间来读取轴。变量 margin 保存一个对象,该对象具有您想要在可视化周围留下的空间量(以像素为单位)。 |
| var width = 480 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
| width 和 height 变量保存 SVG 画布的宽度和高度——边距。 |
| var data = [``{"amount": 5, "name": "column1"},``...
T3】 | 变量数据包含一个对象数组;在这种情况下,每个对象中有两个键/值对。 |
| var x = d3.scaleBand()``.domain(data.map(function(d) { return d.name; }))``.range([0, width])
T3】 | 变量 x 保存 x 轴的比例计算。D3.js 函数 scaleBand()用于非数字数据,如标签或序号数据。输入域是名称数据;它需要一个参数,数据。它遍历数据并计算出有多少个值。 |
| var y = d3.scaleLinear()``.domain([0, d3.max(data, function(d) { return +d.amount; })])
T2】 | y 变量保存 y 轴的刻度。这次使用 scaleLinear()函数,因为数据是数字。输入域是数据集中从 0 到最大数的数组。d3.max()函数在数据集中寻找最大的数字。 |
| var dataviz = d3.select("#viz").append("svg")``.attr("width", width + margin.left + margin.right)``.attr("height", height + margin.top + margin.bottom)``.append("g")
T4】 | d3.select()函数允许您选择一个 DOM 元素来附加可视化。append()函数向元素添加一个 SVG。接下来的两个 attr()函数设置元素的宽度和高度。添加从宽度和高度中移除的边距。append(“g”)将“g”元素添加到可视化中。“g”元素不是 D3.js 特有的;它是一个容器元素,允许您将图形元素组合在一起。 |
| dataviz.selectAll(".bar")
| selectAll()函数选择所有的条形对象,即使还没有任何对象,它为条形创建一个占位符。 |
| .data(data)
| data()函数将数据附加到可视化中。 |
| .enter()
.append("rect")
| enter()函数是更新模式的一部分,append 添加了一个 SVG。 |
| .attr("class", "bar")
| 一个类被添加到每个酒吧,所以他们可以被设计,它也允许你再次选择他们。 |
| .attr("x", function(d) { return x(d.name); })
| 这将设置每个条形的 x 轴位置。 |
| .attr("width", x.bandwidth())
| 宽度属性设置每个条形的宽度。它是使用前面设置的 x 刻度计算出来的。它知道有多少数据项和可视化的宽度。 |
| .attr("y", function(d) { return y(+d.amount); })
| 这将设置矩形顶部的位置。 |
| +d.amount
| 您可以在值前使用+将数量数据转换为数字。有时你认为数据是一个数字,但它实际上是一个字符串。 |
| .attr("height", function(d) { return height - y(+d.amount); });
| 这设置了酒吧的高度。SVG 的坐标从左上角的 0 0 开始,任何高度都是从上到下。在此图中,您希望条形从轴的底部向上增长,高度–y(+d . amount)解决了这个问题。 |
| dataviz.append("g")``.attr("transform", "translate(0," + height + ")")
T2】 | 这将向包含 x 轴的 SVG 追加一个新组。D3.js 有一个 axisBottom()函数,它在 SVG 的底部创建一个水平轴。 |
| dataviz.append("g")
.call(d3.axisLeft(y));
| 这将向 SVG 追加一个包含 y 轴的新组。D3.js 有一个函数 axisLeft(),它在 SVG 的左边创建一个垂直轴。 |
表 7-1 解释了 bar_chart.html 中的代码。
方法链接
您可能已经注意到,在 D3.js 中,有一些函数是用“.”一个接一个地调用的他们之间。这被称为方法链接,在 JavaScript 和 JavaScript 库中使用。代码“dataviz . append(“g”)。attr("transform "," translate(0,"+ height +")。call(D3 . axis bottom(x));"是一个接一个调用的三个函数,append()、attr()和 call()。它使代码更容易阅读,并创建自然组合在一起的函数调用块。
用 D3.js 可视化来自 Arduino 的数据
本章将使用与第 6 章相同的 Arduino 设置和相同的基础 JavaScript 代码。JavaScript 将被更新以包含新的可视化。图 7-4 显示了这一章的最终结果,一个条形图显示了第一个问题“在今晚的活动中,你和新认识的人说话了吗?”
图 7-4
The web application with a bar chart Set Up the Arduino
Arduino 的设置与第 6 章中的设置完全相同。Arduino 的设置如图 6-3 所示。一旦组件连接完毕,用清单 6-1 中的代码刷新 Arduino。您想要使用原始代码,而不是在第 6 章中添加的更新。
Set Up the Node.js Server
该代码也将基于第 6 章中的原始代码。应用程序的目录结构将是:
/chapter_07
/node_modules
/public
/css
main.css
/javascipt
main.js
/views
index.ejs
index.js
创建 skeleton Node.js 应用程序与前几章相同:
- 创建一个新文件夹来存放应用程序。我把我的叫做 chapter_07。
- 打开命令提示符(Windows 操作系统)或终端窗口(Mac)并导航到新创建的文件夹。
- 在正确的目录中,键入 npm init 创建一个新的应用程序;您可以按下 return 键浏览每个问题,或者对它们进行更改。
- 您现在可以开始添加必要的库;要在命令行下载 Express.js,请键入 npm install express@4.15.3 - save。
- 然后安装 ejs,键入 npm install ejs@2.5.6 save。
- 下载完成后,安装串口。在 Mac 上键入 NPM install serial port @ 4 . 0 . 7–save,在 Windows PC 上键入 NPM install serial port @ 4 . 0 . 7-build-from-source。
- 然后最后安装 socket.io,输入 npm install socket.io@1.7.3 - save。
在 index.js 中,文件复制清单 6-2 中的代码。你想使用代码的原始清单,而不是在第 6 章中更新的版本。将清单 6-3 中的代码复制到 index.ejs 文件,然后将清单 6-4 中的 CSS 复制到 main.css 文件,最后将清单 6-5 中的代码复制到 main.js 文件。
确保在新的 serial port()函数中将更新为您自己的串口。
现在,您应该已经在 chapter_7 应用程序中复制了上一章的基本设置。您可以通过在控制台窗口中导航到 chapter_07 应用程序并使用 nodemon index.js 或 node index.js 启动该应用程序来测试您是否拥有它。只要连接了 Arduino,您就应该能够与电位计和按钮进行交互,并在打开浏览器并转到 http://localhost:3000 时看到结果。
Update the Application
现在您可以升级 main.js 了。打开文件,添加清单 7-2 中粗体显示的代码。
(function(){
var socket = io();
var accumulatorArrayA0 = [0,0,0,0,0,0,0,0,0,0,0];
var accumulatorArrayA1 = [0,0,0,0,0,0,0,0,0,0,0];
var margin = {top: 20, right: 20, bottom: 40, left: 40};
var width = 480 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([0, width], .1)
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
var bars = d3.select("#bar-chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
bars.selectAll(".bar")
.data(accumulatorArrayA0)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d, i) { return x(i); })
.attr("width", x.bandwidth())
.attr("y", function(d) {return y(d); })
.attr("height", function(d) { return height - y(+d); });
bars.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
bars.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y)
.ticks(0));
bars.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top + 20) + ")")
.style("text-anchor", "middle")
.text("score");
bars.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("frequency");
socket.on("bar-data", function(data){
var current = data.dataKey;
var svgBar = document.getElementById(current);
var newWidth = data.dataString * 40;
svgBar.setAttribute("width", newWidth);
currentInputValue(data);
addRemoveClass("add");
});
socket.on("button-data", function(data){
accumulatorArrayA0[data[0]] = accumulatorArrayA0[data[0]] + 1;
accumulatorArrayA1[data[1]] = accumulatorArrayA1[data[1]] + 1;
updateBar(accumulatorArrayA0);
addRemoveClass("remove");
});
function updateBar(data){
x.domain(d3.range(data.length));
y.domain([0, d3.max(data)]);
var rect = bars.selectAll(".bar")
.data(data);
rect.enter().append("rect");
rect.attr("class", "bar")
.transition()
.duration(1000)
.attr("x", function(d, i) { return x(i); })
.attr("width", x.bandwidth())
.attr("y", function(d) {return y(d); })
.attr("height", function(d) { return height - y(+d); });
bars.select(".x.axis")
.transition()
.duration(1000)
.call(d3.axisBottom(x));
bars.select(".y.axis")
.transition()
.duration(1000)
.call(d3.axisLeft(y)
.ticks(d3.max(data))
.tickFormat(d3.format("d")));
}
function addRemoveClass(action){
var buttonResponse = document.getElementById("bar-A0").getElementsByClassName("text-block-response")[0];
buttonResponse.classList[action]("hidden");
buttonResponse = document.getElementById("bar-A1").getElementsByClassName("text-block-response")[0];
buttonResponse.classList[action]("hidden");
}
function currentInputValue(data){
var targetP = document.getElementById("bar-" + data.dataKey).getElementsByClassName("text-block")[0].getElementsByTagName("p")[0];
targetP.innerHTML = data.dataString;
}
})();
Listing 7-2main.js
代码解释
应用程序中使用的 D3.js 类似于清单 7-1 中的例子,但是有一些新的功能。表 7-2 详细介绍了 main.js 中的代码
表 7-2
main.js
explained
| .ticks(0));
| 这个函数在 y 轴上创建刻度,因为在创建图形时没有任何数据,并且它被设置为 0;这个会在有数据的时候更新。 |
| bars.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top + 20) + ")")
.style("text-anchor", "middle")
| 这些函数为 x 轴添加并对齐标签,对于 y 轴也有一组类似的函数。 |
| updateBar(data);
| 当按下按钮时,需要用新数据更新条形图。更新的代码在一个名为 updateBar()的函数中,传递给它的是数据数组。 |
| x.domain(d3.range(data.length));
y.domain([0, d3.max(data)]);
| 以前,x 和 y 的定义域和值域是同时声明的。现在,随着新数据添加到阵列中,域将不断变化。这意味着 x 和 y 的定义域也需要改变。在这种情况下,x 的数据长度不会改变,但数组中的最大值会改变。这用于改变条形的高度,因此 y.domain 从 0 到数组中的最大值。这使用了 d3.max()函数,它可以将一个数组作为它的数据,并计算出数组中的最大值。 |
| .transition()
.duration(1000)
| 这些函数使条形的新值从旧值变为动画,持续时间以毫秒为单位。 |
| .ticks(d3.max(data))
| 您需要 y 轴上的刻度,但是最大数量需要随着数组中最大数量的增加而改变。使用 d3.max()函数意味着刻度将始终与数组中的最大数量相同。 |
| .tickFormat(d3.format("d")));
| 有许多方法可以格式化您的分笔成交点;使用“d”使它们成为整数。 |
您需要将可视化的创建与更新分开。如果不这样做,每次添加新数据时,都会创建新的 SVG。通过分离创建和更新,您可以用新数据更新同一个 SVG。
Update the Front End
更新前端不需要做太多。打开 index.ejs 文件,添加清单 7-3 中的粗体代码。这是第六章的代码,有小的更新,所以我没有完整地写旧代码。
<!DOCTYPE html>
<html>
<head>
...
<link href="/css/main.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="https://d3js.org/d3.v4.js"></script>
</head>
<body>
<header>
<h1>EVENT METRICS</h1>
<h2>getting information through an Arduino</h2>
</header>
<div id="content">
<h2>AT TONIGHTS EVENT DID YOU ...</H2>
<p>Answer the questions by turning the knobs, to submit your answer press the button.</p>
...
<div class="text-block-response hidden">
<h3>Thanks<h3>
<p></p>
</div>
</div>
<div id="bar-chart">
<h2>Talk to someone new?</h2>
<p>did people meet new people tonight?</p>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="javascript/main.js"></script>
</body>
</html>
Listing 7-3Index.ejs
更新将包括 D3.js 库,并添加一个 div 来保存可视化。
Update the CSS
打开 main.css 文件;您应该已经从第 6 章复制了 CSS,所以只需要添加以下代码:
.bar {
fill: #6BCAE2;
}
这为图表中的条形添加了颜色。
在您的浏览器上,如果您刷新页面,您应该能够看到条形上的颜色。
整理代码
您可能已经注意到,添加 D3.js 代码已经创建了许多全局变量。出于多种原因,使用全局变量并不是一个好主意,这些原因包括:
- 它们位于全局名称空间中。很容易忘记你称之为变量的东西,并创建多个同名的变量。这会给你带来意想不到的结果。任何函数也可以使用该变量。
- 如果您引入其他库,它们可能与您的全局变量同名。
- 很难看出什么变量属于什么函数。
创建可视化效果的代码与页面其余部分使用的代码非常不同,因此它很适合在单独的 JavaScript 文件中拥有自己的空间。您确实需要将数据从 main.js 传递到可视化,这可以通过多种方式完成。在这一章中,你将使用揭示模块模式来做这件事。
显示模块模式
JavaScript 中有许多编程模式。启示模块模式就是其中之一。它使用一个变量来保存一个立即调用的函数表达式。函数在加载时被调用。在这个函数中,您可以创建变量和函数,它们被封装在 main 函数中。您可以允许在模块外部访问这些函数和变量。在函数结束时返回它们可以做到这一点。任何未返回的函数或变量都不能在模块外部调用。
分离数据可视化
在 chapter_07 应用程序中,在 public/javascript 文件夹中创建一个名为 BarChart.js 的新文件。这意味着 chapter_07 应用程序的目录结构如下所示:
/chapter_07
/node_modules
/public
/css
main.css
/javascipt
main.js
BarChart.js
/views
index.ejs
index.js
Use the Revealing Module Pattern
打开 BarChart.js 并复制清单 7-4 中的代码。
var BarChart = (function(){
var margin = {top: 20, right: 20, bottom: 40, left: 40};
var width = 480 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var x;
var y;
var bars;
function setup(data){
x = d3.scaleBand()
.range([0, width], .1)
.padding(0.1);
y = d3.scaleLinear()
.range([height, 0]);
bars = d3.select("#bar-chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
bars.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d, i) { return x(i); })
.attr("width", x.bandwidth())
.attr("y", function(d) {return y(d); })
.attr("height", function(d) { return height - y(+d); });
bars.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
bars.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y)
.ticks(0));
bars.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top + 20) + ")")
.style("text-anchor", "middle")
.text("score");
bars.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("freqency");
}
function updateBar(data){
x.domain(d3.range(data.length));
y.domain([0, d3.max(data)]);
var test = d3.max(data);
var rect = bars.selectAll(".bar")
.data(data);
rect.enter().append("rect");
rect.attr("class", "bar")
.transition()
.duration(1000)
.attr("x", function(d, i) { return x(i); })
.attr("width", x.bandwidth())
.attr("y", function(d) {return y(d); })
.attr("height", function(d) { return height - y(+d); });
bars.select(".x.axis")
.transition()
.duration(1000)
.call(d3.axisBottom(x));
bars.select(".y.axis")
.transition()
.duration(1000)
.call(d3.axisLeft(y)
.ticks(test)
.tickFormat(d3.format("d")));
}
return{
setup: setup,
updateBar: updateBar
}
})();
Listing 7-4BarChart.js
代码解释
应用程序中使用的 D3.js 类似于清单 7-1 中的例子,但是有一些新的功能。表 7-3 解释了 BarChart.js 中的代码
表 7-3
BarChart.js
explained
| var BarChart = (function(){
})();
| 创建一个匿名函数来保存用于创建可视化的变量和函数。它保存在一个名为条形图的变量中。它在加载时调用自己。 |
| var margin = {top: 20, right: 20, bottom: 40, left: 40};
var width = 480 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var x;
var y;
| BarChart 内有不同函数会用到的变量,所以在 BarChart.js 内全局添加,只能在变量 BarChart 内保存的函数范围内看到。 |
| function setup(data){}
| setup 函数包含 main.js 中全局可视化的所有设置代码,并将其放在自己的函数中。 |
| function updateBar(data){}
| updateBar()函数具有 updateBar()函数中 main.js 中可视化的所有更新代码。 |
| return{``setup: setup,``updateBar: updateBar
T3】 | 您决定哪些函数和变量可以在函数外部看到。为此,您需要返回函数。“:”之前的名称是其他函数调用该函数的方式,“:”之后的名称是当前函数中的命名函数。您可以返回多个函数和变量,它们之间用“,”分隔。 |
Update Main.js
main.js 文件必须更新;首先,需要删除与可视化连接的所有代码,然后需要添加对 setup()和 updateBar()函数的调用。从清单 7-2 中打开 main.js,并用清单 7-5 中的代码更新它。
(function(){
var socket = io();
var accumulatorArrayA0 = [0,0,0,0,0,0,0,0,0,0,0];
var accumulatorArrayA1 = [0,0,0,0,0,0,0,0,0,0,0];
BarChart.setup(accumulatorArrayA0);
socket.on("bar-data", function(data){
var current = data.dataKey;
var svgBar = document.getElementById(current);
var newWidth = data.dataString * 40;
svgBar.setAttribute("width", newWidth);
currentInputValue(data);
addRemoveClass("add");
});
socket.on("button-data", function(data){
accumulatorArrayA0[data[0]] = accumulatorArrayA0[data[0]] + 1;
accumulatorArrayA1[data[1]] = accumulatorArrayA1[data[1]] + 1;
addRemoveClass("remove");
BarChart.updateBar(accumulatorArrayA0);
});
function addRemoveClass(action){
var buttonResponse = document.getElementById("bar-A0").getElementsByClassName("text-block-response")[0];
buttonResponse.classList[action]("hidden");
buttonResponse = document.getElementById("bar-A1").getElementsByClassName("text-block-response")[0];
buttonResponse.classList[action]("hidden");
}
function currentInputValue(data){
var targetP = document.getElementById("bar-" + data.dataKey).getElementsByClassName("text-block")[0].getElementsByTagName("p")[0];
targetP.innerHTML = data.dataString;
}
})();
Listing 7-5Updated main.js code
您会注意到,在创建可视化代码的地方,有两个对新函数的调用。调用该函数的格式如图 7-5 所示。
图 7-5
Calling a function within a revealing module pattern Update Index.ejs
最后,需要更新 index.ejs 文件以包含新的 JavaScript 文件。由于 main.js 使用的是 BarChart.js,所以需要在 main.js 之前调用 BarChart.js,打开清单 7-3 中的 index.ejs,加入清单 7-6 中粗体显示的代码。
<!DOCTYPE html>
<html>
...
<div id="bar-chart">
<h2>Talk to someone new?</h2>
<p>did people meet new people tonigt?</p>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="javascript/BarChart.js"></script>
<script src="javascript/main.js"></script>
</body>
</html>
Listing 7-6Adding BarChart.js to index.ejs
如果您正在运行 localhost,请刷新浏览器或重新启动服务器。该页面应该以完全相同的方式工作,但现在代码更加模块化,这使得它更安全、更易于阅读。
您可以添加第二个条形图来显示第二个电位计的数据。
摘要
在本章中,你开始使用 D3.js 来可视化来自 Arduino 的数据。您还尝试了一些新的 JavaScript 概念,现在应该对 JavaScript 的结构有了更好的理解。在下一章中,您将创建一个仪表板,并使用 D3.js 创建圆环图。
版权属于:月萌API www.moonapi.com,转载请注明出处