Preamble
Let's get access to the D3.js library so that we can begin. In this case, we'll be including the library using the HTML <script>
tag.
<script src="https://d3js.org/d3.v7.js"></script>
Introduction
Up until this section, we've been making use of several basic shapes as they are defined in the W3C specification. These include the rect
, circle
, and ellipse
1. In this section, however, we'll look at how we can create custom and complex shape using the SVG path
element2.
Let's use the creation of a zigzag as an example. Whilst you could say that it's possible to draw a zigzag using multiple rect
elements at different positions and rotations (in the same way that you could draw any image with an unbounded number of rect
elements behaving as pixels), it is certainly an infeasible and inefficient exercise.
This is where the SVG path
element comes in. A path describes an outline of some shape that can be filled and/or stroked. Alternatively, a path could be left without a fill or stroke, so that it can be used to position text or define an animation path.
Paths are drawn as if a pen has been placed on paper at a current point, whereby following instructions move that pen in either lines or curves.
<svg>
<path d="M20,60L60,20L100,60L140,20L180,60L220,20" stroke="black" fill="none"></path>
</svg>
We can see the output of the above SVG markup is a zigzag pattern. This shape has been specified using the d
(data) property. In this case, we've used a series of moveto (M
) and lineto (L
) commands to construct our shape using coordinates.
We can see that the fill
property has been explicitly set to "none"
. Otherwise, the default behaviour would be to fill the shape.
<svg>
<path d="M20,60L60,20L100,60L140,20L180,60L220,20" stroke="black"></path>
</svg>
As we can see, this is not the desired output for our zigzag shape.
Let's see how we can create the same complex shape using D3.js.
A Container for the Output
This is where you will see the output of the code cells that follow it, provided they are referencing the corresponding id
.
<div id="container"></div>
Creating an Empty SVG
We'll create a new detached <svg>
element and use the returned selection throughout the rest of this section.
svg = d3.create("svg");
Creating a Complex Shape with Paths
To create the same zigzag as above we need to complete three steps.
- Populate a data structure with our coordinates.
- Construct a line generator with
d3.line()
3. - Generate a line by passing our populated data structure to the line generator.
Let's get started.
Populating a 2D Array
We'll use a simple 2D array for our data structure, passing in the coordinates that specify our zigzag shape. We'll store this in the data
variable.
var data = [
[20, 60],
[60, 20],
[100, 60],
[140, 20],
[180, 60],
[220, 20]
];
Constructing the Line Generator
Next, we'll need to construct our line generator using d3.line()
. We'll store this in the lineFun
variable.
var lineFun = d3.line();
Generate the Path
Then we'll generate the zigzag line by passing our path data into our line generator, i.e. lineFun(data)
. This will be used to set the d
(or data) property of our path
element.
To create a <path>
element with D3.js we can invoke the d3.append(name)
function on our svg
selection and pass in the name of the element. We'll also specify the stroke
and fill
properties.
var line = svg.append("path")
.attr("d", lineFun(data))
.attr("stroke", "black")
.attr("fill", "none");
Appending to the Container
Finally, let's append everything to our container.
d3
.select("#container")
.append(() => svg.node());
We can see the output by checking on our container with the corresponding id, which in this case is where id=container
.
Conclusion
If we inspect the HTML, we will see the <svg>
and <path>
elements have been added to the <div>
where the id=container
. We can also see that the <path>
element's d
attribute contains the path data that specifies our zigzag. We also have the stroke
set to black, and the fill
set to none
.
<div id="container">
<svg>
<path d="M20,60L60,20L100,60L140,20L180,60L220,20"
stroke="black" fill="none"></path>
</svg>
</div>