首先你需要在计算机上安装Node和npm。

数据的可视化表示是传递复杂信息的最有效手段之一,D3.js提供了创建这些数据可视化的强大工具和灵活性。

D3.js是一个JavaScript库,用于使用SVG,HTML和CSS在Web浏览器中生成动态的交互式数据可视化。

在本教程中,我们将探讨如何使用D3.js和Pusher Channels构建实时图形。如果您在阅读本教程时想要使用代码,请查看此GitHub存储库,其中包含代码的最终版本。

准备

要完成本教程,您需要安装Node.jsnpm。我在创建本教程时使用的版本如下:

  • Node.js v10.4.1
  • npm v6.3.0

您还需要在计算机上安装http-server。它可以通过运行以下命令通过npm安装:npm install http-server。

虽然不需要Pusher知识,但如果熟悉它后,对学习JavaScript和D3.js会很有帮助。

开始

首先,为我们要构建的应用程序创建一个新目录。将其称为实时图形或任何您喜欢的图形。在新创建的目录中,创建一个新的index.html文件并粘贴以下代码:

  1. //index.html
  2. <!DOCTYPE html>
  3. <hml lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  8. <link rel="stylesheet" href="style.css">
  9. <title>Realtime D3 Chart</title>
  10. </head>
  11. <body>
  12. <script src="https://js.pusher.com/4.2/pusher.min.js"></script>
  13. <script src="https://d3js.org/d3.v5.min.js"></script>
  14. <script src="app.js"></script>
  15. </body>
  16. </html>

如您所见,HTML文件只是提取构建图形所需的样式和脚本。我们正在利用D3.js来构建图表,并使用Pusher来添加实时功能。app.js文件是应用程序前端代码的写入位置。

在我们开始实现图表之前,让我们在style.css中添加应用程序的样式:

  1. // style.css
  2. html {
  3. height: 100%;
  4. box-sizing: border-box;
  5. padding: 0;
  6. margin: 0;
  7. }
  8. *, *::before, *::after {
  9. box-sizing: inherit;
  10. }
  11. body {
  12. height: 100%;
  13. font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
  14. overflow: hidden;
  15. background: linear-gradient(135deg, #ffffff 0%,#e8f1f5 100%);
  16. }
  17. .container {
  18. position: absolute;
  19. padding: 20px;
  20. top: 50%;
  21. left: 50%;
  22. background-color: white;
  23. border-radius: 4px;
  24. transform: translate(-50%, -50%);
  25. box-shadow: 0px 50px 100px 0px rgba(0,0,102,0.1);
  26. text-align: center;
  27. }
  28. .container h1 {
  29. color: #333;
  30. }
  31. .bar {
  32. fill: #6875ff;
  33. border-radius: 2px;
  34. }
  35. .bar:hover {
  36. fill: #1edede;
  37. }
  38. .tooltip {
  39. opacity: 0;
  40. background-color: rgb(170, 204, 247);
  41. padding: 5px;
  42. border-radius: 4px;
  43. transition: opacity 0.2s ease;
  44. }

安装服务器依赖项

假设您安装了Node和npm,请运行以下命令来安装应用程序的服务器组件所需的所有依赖项:

  1. npm install express dotenv cors pusher

Pusher 设置

前往Pusher网站并注册一个免费帐户。选择侧栏上的Channels apps,然后点击Create Channels app以创建新应用。

创建应用程序后,从API Keys选项卡中检索凭据,然后在项目目录根目录中创建一个variables.env文件,将以下内容添加到这个文件中。

  1. // variables.env
  2. PUSHER_APP_ID=<your app id>
  3. PUSHER_APP_KEY=<your app key>
  4. PUSHER_APP_SECRET=<your app secret>
  5. PUSHER_APP_CLUSTER=<your app cluster>

设置服务器

现在我们已经安装了相关的依赖项并且已经设置了我们的Pusher帐户,我们可以开始构建服务器。

在项目目录的根目录中创建一个名为server.js的新文件,并粘贴以下代码:

  1. // server.js
  2. require('dotenv').config({ path: 'variables.env' });
  3. const express = require('express');
  4. const cors = require('cors');
  5. const poll = [
  6. {
  7. name: 'Chelsea',
  8. votes: 100,
  9. },
  10. {
  11. name: 'Arsenal',
  12. votes: 70,
  13. },
  14. {
  15. name: 'Liverpool',
  16. votes: 250,
  17. },
  18. {
  19. name: 'Manchester City',
  20. votes: 689,
  21. },
  22. {
  23. name: 'Manchester United',
  24. votes: 150,
  25. },
  26. ];
  27. const app = express();
  28. app.use(cors());
  29. app.get('/poll', (req, res) => {
  30. res.json(poll);
  31. });
  32. app.set('port', process.env.PORT || 4000);
  33. const server = app.listen(app.get('port'), () => {
  34. console.log(Express running PORT ${server.address().port});
  35. });

保存文件并从项目目录的根目录运行节点server.js以启动服务器。

设置前端应用程序

应用程序的前端将写在我们之前引用的app.js文件中。在项目目录的根目录中创建此文件,并在其中粘贴以下代码:

  1. // app.js
  2. // set the dimensions and margins of the graph
  3. const margin = { top: 20, right: 20, bottom: 30, left: 40 };
  4. const width = 960 - margin.left - margin.right;
  5. const height = 500 - margin.top - margin.bottom;
  6. // set the ranges for the graph
  7. const x = d3
  8. .scaleBand()
  9. .range([0, width])
  10. .padding(0.1);
  11. const y = d3.scaleLinear().range([height, 0]);
  12. // append the container for the graph to the page
  13. const container = d3
  14. .select('body')
  15. .append('div')
  16. .attr('class', 'container');
  17. container.append('h1').text('Who will win the 2018/19 Premier League Season?');
  18. // append the svg object to the body of the page
  19. // append a 'group' element to 'svg'
  20. // moves the 'group' element to the top left margin
  21. const svg = container
  22. .append('svg')
  23. .attr('width', width + margin.left + margin.right)
  24. .attr('height', height + margin.top + margin.bottom)
  25. .append('g')
  26. .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  27. // Create a skeleton structure for a tooltip and append it to the page
  28. const tip = d3
  29. .select('body')
  30. .append('div')
  31. .attr('class', 'tooltip');
  32. // Get the poll data from the /poll endpoint
  33. fetch('http://localhost:4000/poll')
  34. .then(response => response.json())
  35. .then(poll => {
  36. // add the x Axis
  37. svg
  38. .append('g')
  39. .attr('transform', 'translate(0,' + height + ')')
  40. .attr('class', 'x-axis')
  41. .call(d3.axisBottom(x));
  42. // add the y Axis
  43. svg
  44. .append('g')
  45. .attr('class', 'y-axis')
  46. .call(d3.axisLeft(y));
  47. update(poll);
  48. });
  49. function update(poll) {
  50. // Scale the range of the data in the x axis
  51. x.domain(
  52. poll.map(d => {
  53. return d.name;
  54. })
  55. );
  56. // Scale the range of the data in the y axis
  57. y.domain([
  58. 0,
  59. d3.max(poll, d => {
  60. return d.votes + 200;
  61. }),
  62. ]);
  63. // Select all bars on the graph, take them out, and exit the previous data set.
  64. // Enter the new data and append the rectangles for each object in the poll array
  65. svg
  66. .selectAll('.bar')
  67. .remove()
  68. .exit()
  69. .data(poll)
  70. .enter()
  71. .append('rect')
  72. .attr('class', 'bar')
  73. .attr('x', d => {
  74. return x(d.name);
  75. })
  76. .attr('width', x.bandwidth())
  77. .attr('y', d => {
  78. return y(d.votes);
  79. })
  80. .attr('height', d => {
  81. return height - y(d.votes);
  82. })
  83. .on('mousemove', d => {
  84. tip
  85. .style('position', 'absolute')
  86. .style('left', ${d3.event.pageX + 10}px)
  87. .style('top', ${d3.event.pageY + 20}px)
  88. .style('display', 'inline-block')
  89. .style('opacity', '0.9')
  90. .html(
  91. <div><strong>${d.name}</strong></div> <span>${d.votes} votes</span>
  92. );
  93. })
  94. .on('mouseout', () => tip.style('display', 'none'));
  95. // update the x-axis
  96. svg.select('.x-axis').call(d3.axisBottom(x));
  97. // update the y-axis
  98. svg.select('.y-axis').call(d3.axisLeft(y));
  99. }

在上面的代码块中,我们使用通过/ poll端点接收的初始数据创建了一个基本条形图。如果您熟悉D3的工作原理,那么您应该熟悉这些代码。我在代码的关键部分添加了注释,以指导您构建图表的方式。

在新终端中,启动开发服务器以提供index.html文件:

  1. npx http-server

我在这里使用http-server,但你可以使用你想要的任何服务器。您甚至可以直接在浏览器中打开index.html。

此时,您的图表应如下所示:

使用Pusher实时更新图表

让我们确保轮询的更新可以通过Pusher Channels实时反映在应用程序的前端中。将以下代码粘贴到app.js文件的末尾。

  1. // app.js
  2. const pusher = new Pusher('<your app key>', {
  3. cluster: '<your app cluster>',
  4. encrypted: true,
  5. });
  6. const channel = pusher.subscribe('poll-channel');
  7. channel.bind('update-poll', data => {
  8. update(data.poll);
  9. });

在这里,我们打开了与Channels的连接,并使用Pusher的subscribe()方法订阅了一个名为poll-channel的新频道。通过bind方法监听轮询更新,并在收到更新后使用最新数据调用update()函数,以便重新呈现图形。

不要忘记使用Pusher帐户信息中心中的相应详细信息替换占位符。

从服务器触发更新

我们将模拟每秒更新一次的轮询,并在数据发生变化时使用Pusher触发更新,以便轮询的订阅者(客户端)可以实时接收更新的数据。

在其他导入下面的server.js顶部添加以下代码:

  1. const Pusher = require('pusher');
  2. const pusher = new Pusher({
  3. appId: process.env.PUSHER_APP_ID,
  4. key: process.env.PUSHER_APP_KEY,
  5. secret: process.env.PUSHER_APP_SECRET,
  6. cluster: process.env.PUSHER_APP_CLUSTER,
  7. encrypted: true,
  8. });
  9. function getRandomNumber(min, max) {
  10. return Math.floor(Math.random() * (max - min) + min);
  11. }
  12. function increment() {
  13. const num = getRandomNumber(0, poll.length);
  14. poll[num].votes += 20;
  15. }
  16. function updatePoll() {
  17. setInterval(() => {
  18. increment();
  19. pusher.trigger('poll-channel', 'update-poll', {
  20. poll,
  21. });
  22. }, 1000);
  23. }

然后将/ poll端点更改为如下所示:

  1. app.get('/poll', (req, res) => {
  2. res.json(poll);
  3. updatePoll();
  4. });

/ poll路由将初始轮询数据发送到客户端并调用updatePoll()函数,该函数以三秒为间隔递增随机俱乐部的投票,并触发我们在最后一步中在客户端上创建的轮询频道的更新。

通过从项目目录的根目录运行节点server.js,终止服务器并重新启动它。此时,您应该有一个实时更新的条形图。

结论

您已经看到了使用D3.js创建条形图的过程以及如何使用Pusher Channels实时创建条形图。

我们已经为Pusher和D3提供了一个简单的用例,但其中一个仅仅是表面上的问题。我建议深入研究docs,了解更多有关Pusher及其他功能的信息。

谢谢阅读!您可以在GitHub存储库中找到本教程的完整源代码。

使用D3.js构建实时图形的更多相关文章

  1. springboot2.0+Neo4j+d3.js构建知识图谱

    Welcome to the Neo4j wiki! 初衷这是一个知识图谱构建工具,最开始是对产品和领导为了做ppt临时要求配合做图谱展示的不厌其烦,做着做着就抽出一个目前看着还算通用的小工具 技术栈 ...

  2. 使用Node,Vue和ElasticSearch构建实时搜索引擎

    (译者注:相关阅读:node.js,vue.js,Elasticsearch) 介绍 Elasticsearch是一个分布式的RESTful搜索和分析引擎,能够解决越来越多的用例. Elasticse ...

  3. SVG基础图形和D3.js

    使用D3.js画一个SVG 的 圆 circle 可以使用如下代码创建: <svg width="50" height="50"> <circ ...

  4. D3.js学习笔记(六)——SVG基础图形和D3.js

    目标 在这一章,我们将会重温SVG图形,学习如何使用D3.js来创建这些图形. 这里会包括前面例子中的SVG基础图形以及如何使用D3.js设置图形的属性. 使用D3.js画一个SVG 的 圆 circ ...

  5. D3.js的v5版本入门教程(第十二章)—— D3.js中各种精美的图形

    D3.js的v5版本入门教程(第十二章) D3中提供了各种制作常见图形的函数,在d3的v3版本中叫布局,通过d3.layout.xxx,来新建,但是到了v5,新建一个d3中基本的图形的方式变了(我也并 ...

  6. D3.js学习笔记(三)——创建基于数据的SVG元素

    目标 在这一章,你将会使用D3.js,基于我们的数据来把SVG元素添加到网页中.这一过程包括:把数据绑定到元素上,然后在使用这些元素来可视化我们的数据. 注意:不同于前几章,我们从一个完整的代码开始, ...

  7. D3.js 力导向图的显示优化

    D3.js 作为一个前端,说到可视化除了听过 D3.js 的大名,常见的可视化库还有 ECharts.Chart.js,这两个库功能也很强大,但是有一个共同特点是封装层次高,留给开发者可设计和控制的部 ...

  8. NoSQL:如何使用NoSQL架构构建实时广告系统

    JDNoSQL平台是什么 JDNoSQL平台是一个分布式面向列的KeyValue毫秒级存储服务,存储结构化数据和非机构化数据,支持随机读写与更新,灵活的动态列机制,架构上支持水平扩容,提供高并发.低延 ...

  9. D3.js部署node环境开发

    总结一段D3.js部署node环境的安装过程 准备阶段: 首先电脑上要安装node环境,这个阶段过滤掉,如果node环境都不会装,那就别玩基于node环境搞的其他东西了. 搭建环境: 我在自己的F:系 ...

随机推荐

  1. OrderValidator

    package org.linlinjava.litemall.core.validator; import javax.validation.Constraint; import javax.val ...

  2. D - Association for Control Over Minds Kattis - control (并查集+STL)

    You are the boss of ACM (Association for Control over Minds), an upstanding company with a single go ...

  3. [HNOI2003]操作系统 优先队列用法

    题:https://www.cometoj.com/problem/1046 #include<bits/stdc++.h> using namespace std; typedef lo ...

  4. Descriptive Measures for Populations|Parameter|Statistic|standardized variable|z-score

    3.4 Descriptive Measures for Populations; Use of Samples For a particular variable on a particular p ...

  5. JS中获得指定日期前或后几天对应的日期

    var d = new Date(); d.setDate(d.getDate() - 2); console.log(d.toString()); // First of month var c = ...

  6. 64)PHP,变量的生命周期

    在20day  05 假如我目前在的地址是上面的那个index.php?p=back&c=Admin&a=check  这个请求里面申请的所有事 或者是你申请的所有变量或者是全局变量都 ...

  7. Grails Controller - respond 方法

    基本用法 官方文档:http://docs.grails.org/latest/ref/Controllers/respond.html 为当前 respond 语句所在 action 所对应的页面返 ...

  8. django框架进阶-中间件-长期维护

    ##################    为什么使用中间件?      ####################### 先说几个需求, 1,url的白名单,url=[ "XX"] ...

  9. Android Studio调用系统隐藏接口EthernetManager

    google source签名文件参考:https://android.googlesource.com/platform/build/+/donut-release/target/product/s ...

  10. 为什么说iPhone无望恢复中国市场?

    直到现在还记得,iPhone 4在国内当时引发的追捧狂潮.彼时iPhone 4绝对是一机难求,上至土豪下至学生都以拥有iPhone 4为荣.发售接近一年后仍然需要加价,价格动辄达到七八千元,真正成为了 ...