Hello, everyone brave little friends, everyone, I am your mouth strong five, healthy, not brain disease.


 

I have a wealth of hair loss skills, can let you leap to become a senior big coffee.

A look will be written on the scrap is my theme, food to dig the feet is my characteristics, humble with a trace of strong, silly people have silly blessing is the biggest comfort to me.

Welcome to the “d3.js” hand-drawn section line chart of Xiao 5’s essay series.

Writing in the front

Hand in the code link portal – AJUN 568

Feet with final renderings

View before remind

👺 In this paper, the ultimate goal is to realize the above figure, and all the processes serve for the results, rather than the systematic learning of SVG and D3.js.

takeaway

👺 friends, do you have the same functional appeal with me? It is not easy to meet our needs using the mainstream chart library; My friend, do you want to complete the corresponding function development in a short period of time as eager as I do? If you feel the same way in front of the screen, then D3 is a good choice because it’s easy to use and can be customized to your needs. Let’s go into this fantastic graphic world ❗

The preparatory work

yarn add d3

As of 2021-03, the current latest version is D3 ^6.6.0, with which we begin our journey. If the version is changed, it is recommended to use the latest version.

🗡 anatomy

The proposed data format is as follows

The dataset = [{xValue: x | data Number, yValue: | y data Number, filled: whether a solid point | Boolean,},... [and so on]]

Look 👆 picture

It can be disassembled into the following parts:

  • The axis is $(x, y)$
  • Coordinate point & dotted line from point to coordinate axis
  • The path
  • Tooltip & hover Dotted line from the point to the axis

Introduction to SVG

$d3.js$is the syntactic sugar for $SVG $

Since you have to deal with $SVG $all the time, how can you not get to know this lovely little guy

Viewport => width/height: Specifies the width and height of the canvas

<svg width="800" height="400"></svg>

ViewBox => (x, y, width, height): $(x, y)$point, $width$, $height$, enlarge to the canvas size

The positive direction is as follows:

Let’s get a feel for it in a few pieces of code

🧟 came ️ Code

<svg width="400" height="300" viewBox="0, 0, 400, 300">
  <rect width="20" height="15" fill="red"/>
</svg>

🧟 came ️ Image

🧟 came ️ Code

<svg width="400" height="300" viewBox="10, -150, 400, 300">
  <rect width="20" height="15" fill="red"/>
</svg>

🧟 came ️ Image

In the picture, select the canvas of 400✖️300 in the positive direction at (10, -150) and display it

🧟 came ️ Code

<svg width="400" height="300" viewBox="-10, -10, 40, 30">
  <rect width="20" height="15" fill="red"/>
</svg>

🧟 came ️ Image

This figure corresponds to a 20-fold enlargement of the selected elements

Common label

@params => x, y, width, height, $x, y$ All the examples above are rectangles, so I don’t need to repeat them.

Draw a circle

🧟 came ️ Code

<svg width="400" height="300" viewBox="0, 0, 400, 300">
  <circle cx="100" cy="60" r="50" fill="red"/>
</svg>

$tips:$fill

🧟 came ️ Image

Ellipse draws the ellipse

🧟 came ️ Code

<svg width="400" height="300" viewBox="0, 0, 400, 300">
  <ellipse cx="100" cy="60" rx="80" ry="50" fill="red"/>
</svg>

🧟 came ️ Image

Text Draw Text

🧟 came ️ Code

< SVG width="400" height="300" viewBox="0, 0, 400, 300"> <text x="100" y="60" stroke="red" </text> </ SVG >

$tips:$

Stroke stroke color

Style -> text-anchor: middle by default, middle by end

🧟 came ️ Image

Line draw a line

🧟 came ️ Code

<svg width="400" height="300" viewBox="0, 0, 400, 300">
  <line x1="100" y1="60" x2="300" y2="10" stroke="red" stroke-width="2"/>
</svg>

$tips:$stroke-width

🧟 came ️ Image

Polyline draws polylines

🧟 came ️ Code

<svg width="400" height="300" viewBox="0, 0, 400, 300"> <polyline points="30,140 100,60 300,10 350,50" fill="none" stroke="red" stroke-width="2"/> </ SVG >

$tips:$Remember to fill to none, otherwise the path portion will be filled

🧟 came ️ Image

The path path

🧟 came ️ Code

<svg width="400" height="300" viewBox="0, 0, 400, 300"> <path d="M 20,20 L 100,60 L 20,100 L 60,60 Z" fill="red"/> </ SVG >

🧟 came ️ Image

G Group 👉 Group tags for easy categorization or reuse

Use copy

🧟 came ️ Code

<svg width="400" height="300" viewBox="0, 0, 400, 300"> <path d="M 20,20 L 100,60 L 20,100 L 60,60 Z" fill="red"/> <use href="#arrow" x="200" y="0" /> </ SVG >

🧟 came ️ Image

Defs custom graphics

🧟 came ️ Code

<svg width="400" height="300" viewBox="0, 0, 400, 300"> <defs> <path id=" M 20,20 L 100,60 L 20,100 L 60,60 Z"/> </defs> <use href="#arrow" x="200" y="0" fill="red" /> </svg>

🧟 came ️ Image

hierarchy

🧟 came ️ Code

<svg width="400" height="300" viewBox="0, 0, 400, 300"> <rect x="0" y="0" width="30" height="30" fill="purple"/> <rect x="20" y="5" width="30" height="30" fill="blue"/> <rect x="40" y="10" width="30" height="30" fill="green"/> <rect x="60" y="15" width="30" height="30" fill="pink"/> <rect  x="80" y="20" width="30" height="30" fill="red"/> </svg>

🧟 came ️ Image

✍️ Draw Canvas

Preposition knowledge preparation of almost, guys, enter the main topic ❗

$First$$of$$all$= $HTML $= $First$$of$all$= $HTML $= $HTML

<div id="line"></div>

$Secondly$, we use D3 to create our canvas in this div with a width and height of 400✖️800

  • $d3$is chained
  • selectSelect the element (select $id$)
  • selectAllSelect all elements (select elements, select classes)
  • appendAdditional elements
  • attrAdd attributes
const width = 800
const height = 400

d3.select('#line')
  .append('svg')
  .attr('width', width)
  .attr('height', height)

✍️ Draw the axes

scale

Before we get to the scale, let’s look at a few useful functions:

  • d3.max(arr) return $max$
  • d3.min(arr) return $min$
  • d3.extent(arr) return $[min, max]$

In this paper, linear scale scaleLinear is used

❓ $what$$is$linear scale

👉 Map a continuous interval to another interval (Domin to Range)

Cuihua, on the code

👉 xScale d3.scalelinear () // create a linear scale.domain ([// domin data [x.min, x.max] d3.min(xData), D3.max (xData)]).rangeround ([0, width]) // Range data [0, width]

XAxis draw

Next, we use AxisBottom to create a downward axis and set the scale with the scale call

👉 xAxis d3. AxisBottom (.) scale (xScale wasn't entirely)

The call method is then used to populate the canvas

svg.call(xAxis)

At this point, a rough X-axis is drawn, and music and dance follow

Did you notice anything wrong:

  • The $100 on the right was amputated
  • The axes fill up the whole canvas, and there is no beauty at all
  • Shouldn’t the $x$axis be at the bottom, and there should be space for its good gay friend $y$axis on the left

The core problem with $1, $2 is that there is no white space. If you want white space, add padding. For $3, a translate will do the trick

const padding = { top: 30, right: 30, bottom: 30, left: 30} 👉 xScale -.rangeround ([0, width]) +.rangeround ([0, width]) Width -padding-left. Right]) 👉 SVG SVG.append('g').attr('transform', 'translate(${padding-left}, ${height - padding.bottom})`) .call(xAxis)

🧟 came ️ Image

No.1

  • axis.tickValues([...arr])Use to specify the value to display on the coordinate axis
  • axis.tickFormat()Formats coordinate axis data

$eg:$axis.tickFormat(d3. Format(“,.0f”)), where.0f is the unformatted fraction

No.2

The second point can be understood as adding a piece of data to the left and right, and then connecting the two points. To supplement the data, we need to determine how much to supplement. We introduce a concept of copy number, and define 1 copy as the total length of $1/dataset. Length * 2 $, and the minimum is $1/10 $.

+ const length = dataset.length * 2 + const partDistance = (d3.max(xData) -d3.min (xData)))/(length > 10? 10: length) .domain([ - d3.min(xData), - d3.max(xData) ]) .domain([ + d3.min(xData, item => { + return item - partDistance + }), + d3.max(xData, item => { + return item + partDistance + }) ])

No.3

We have explained defs, g, path and use respectively in the “Brief Introduction to SVG” above.

Cuihua, on the code

Const ArrowPath = 'M4,4 L20,12 L4,20 L8,12 Z' const axisColor = '# FFF 'const ArrowOffsetDistance = 12 SVG .append('defs') .append('g') .attr('id', 'arrowX') .append('path') .attr('d', arrowPath) .attr('fill', axisColor) svg .append('use') .attr('href', '#arrowX') .attr('x', width - padding.right - arrowOffsetDistance) .attr('y', height - padding.bottom - arrowOffsetDistance)

M4,4 L20,12 L4,20 L8,12 Z

  • d3.path()Create a path
  • moveTo(x, y) M
  • lineTo(x, y) L
  • closePath() Z

Therefore, it can also be dynamically generated according to the method

Path () // M4, VETO (4, 4), VETO (4, 4), VETO (4, 4), VETO (4, 4), VETO (4, 4) 12) arrowPath.lineTo(4, 20) arrowPath.lineTo(8, 12) arrowPath.closePath()

No. + up

In order to make the coordinate axis more beautiful, let’s embellish a few strokes at random. The final effect is as follows:

$tip:$select/selectAll to select the element to change the corresponding property (or the graphic property of $style$or $SVG $)

Make up for other good gay friends $Y $Axis Smeda

Marker tags

Used to append elements to a graph, and our axes fit this description very well

  • markerWidth / markerHeightWidth/height
  • refX / refY$x/y$axis offset
  • markerUNitsWhether to allow marker to follow the scale of the connected graph, default StrokeWidth (scale), optional UserSpaceOnUse (no scale)
  • orientRotation Angle, default $auto$, can specify the specific degree of rotation

❓ How to append to drawn graph

👉 Add the following attributes to the graph to be drawn: Mark-end =”url(#id)”, optional [Mark-start, Mark-mid, Mark-end]

Special note: Marker can be understood as acting on a point, so marker-mid has no effect on the connection between two points, and at least three points are required to produce effect

🧟 came ️ Code

<svg width="400" height="300" viewBox="0, 0, 400, > < Path D ="M2,2 L10,6 L2,10 L4,6 Z" fill="red" /> </marker> <line x1="100" x2="300" y2="60" stroke="red" stroke-width="2" marker-end="url(#arrow)" /> </svg>

🧟 came ️ Image

The comparison between the two is as follows:

Draw coordinate points

Coordinate point ❓ draw a circle curse you (draw a circle)

Hollow point ❓ Stroke-width you deserve

Map to axes ❓ Scales of alchemy (map back to scale)

🧟 came ️ Code

🧟 came ️ Image

Draw the line

Point, line, plane, there’s a point we’re starting to connect. With what? I suggest path, but I chose line for no reason. It’s just williness.

💻 knowledge

D3. line().defined() Whether the current point is connected to its adjacent points

Draw a line with a dot, but is the dot really all? Where is the starting point, where is the end point, in the eyes of the children? We complete the data according to the number of copies calculated above, and then plot.

// let Dataset = [] Dataset. Push ({XValue: Dataset [0]. XValue: Dataset [0]. dataset[0].yValue, filled: true }) newDataset.push(... dataset) newDataset.push({ xValue: dataset[dataset.length - 1].xValue + partDistance, yValue: dataset[dataset.length - 1].yValue, filled: true })

The line covers the hollow spot ❓ According to quantum mechanics – $SVG $hierarchy order law, the layer order must be reversed. Change the code order, perfect solution.

❓ If you notice the defined above, however, I am going to unline the defined points with the same x coordinates, rather than empty this point and give it freedom. Clone one, Perfect, solve the problem. And as for cloning which point, along with the mood is good, you say I want both, drag out cut.

// NewDataSet.push (... dataset) + dataset.forEach(item => item.filled ? newDataset.push(item) : newDataset.push(... [item, item]))

🧟 came ️ Code

🧟 came ️ Image

Draws a dotted line from a coordinate point to an axis

Stroke-Dasharray: Used to draw dashed lines, leaving $y$pixels free for every $x$pixel drawn

🧟 came ️ Code

<svg width="400" height="300" viewBox="0, 0, 400, 300">
  <line x1="50" y1="50" x2="350" y2="50" stroke-dasharray="3" fill="none" stroke="red" stroke-width="3"></line>
  <line x1="50" y1="180" x2="350" y2="180" stroke-dasharray="6, 18, 4, 12" fill="none" stroke="red" stroke-width="3"></line>
</svg>

🧟 came ️ Image

Find the point, connect the line, and we have our dotted line

Cuihua, sauerkraut

🧟 came ️ Image

Tooltip

Before you think about Tooltip, throw out a few questions

What does the ❓ tooltip show

The coordinates of the corresponding points 👉

What is the area of ❓ hover

👉 The entire axis

Where is the location shown at ❓

👉 next to the corresponding point

❓ Anything else to pay attention to

👉 Don’t go beyond the boundaries

So let’s summarize

$hover$is the entire area 👉 a $rect$that covers the entire axis and then handles the event

$tooltip$👉 A small $div$that appears next to the corresponding point

$hover$Find the coordinates of the corresponding point on x=c, connect the dotted line to the $x$axis and $y$axis, and display $tooltip$next to the point

Stir up

First append a rect and then align it for event handling. Here we use our familiar and sexy old friends, let’s welcome them to the spotlight:

  svg
    .append('rect')
    .attr('width', areaWidth)
    .attr('height', areaHeight)
    .style('fill', 'none')
    .style('pointer-events', 'all')
    .style('cursor', 'pointer')
    .attr('transform', `translate(${padding.left}, ${padding.top})`)
    .on('mouseover', mouseOver)
    .on('mouseout', mouseOut)
    .on('mousemove', mouseMove)

Next we draw $tooltip$and its dotted lines to $x$axis and to $y$axis.

At this time there will be such a doubt, I haven’t calculated the position, how to draw, see the official don’t worry, please see the following

Mousemove can help us get the offsetX of the point, and then the problem of finding the point becomes that we know $x$and find $y$, and then we find the function expression that relates $x and y$.

$y$=c$=c$=c$=c$=c

$$ (x – x1) * (y2 – y1) = (x2 – x1) * (y – y1) $$

literacy

Pick up the machine gun, aim at the blind spot with a first volley of fire

Scalelinear -> Invert: Inverse mapping (returns the corresponding value in domain based on the given value in range)

const xInvert = xScale.invert(d.offsetX - padding.left)

D3. Bisector () : bisector

To understand this concept, let’s look at its relatives bisectLeft and bisectRight

d3.bisectLeft(arr, x)

Where $arr$is the array to be inserted (sorted), $x$is the value to be inserted, and the return value is the position to be inserted. If $x$is already in the array, insert it into the leftmost corner of the same entry.

const arr1 = [2, 3, 5, 6, 7]
d3.bisectLeft(arr1, 4) // return 2

const arr1 = [2, 3, 3, 6, 7]
d3.bisectLeft(arr1, 3) // return 1

So let’s go back to the bisector, what’s that? It’s actually the same thing, but we’re going to be developing a complex array structure, and we can specify a bisector to compare it to $x$.

const bisect = d3.bisector(d => d.xValue).right
const xInvert = xScale.invert(d.offsetX - padding.left)
const i = bisect(dealDataset, xInvert)

So we know what the interval is, and we know which function to use to find $y$

getBoundingClientRect & getBBox

Selection.node ().getBoundingClientRect() gets the associated attributes of the HTML element

The data presented are as follows:

Selection.node ().getBBox() gets the related properties of the $SVG $element

The book is connected to the text, given the function and the point $x$, we can find the point $y$, and then draw the dotted line from the point to $x$axis and $y$axis. We then populate the display with $tooltip$, repositioning the boundary value according to $getboundingClientRect $to prevent the boundary points from crossing the canvas.

🧟 came ️ Code

🧟 came ️ Image

Great work done, the end of the flower 🎉

Complete code to connect portal Github – ajun568

See 🔗

D3 website

[MDN] SVG Element Reference

[Scott Murrayan] SVG primer

[Ruan Yifeng] SVG image tutorial

[Zhang Xinxu] understand SVG viewport, viewBox preserveAspectRatio zoom

Marker is referenced in an element

D3: What is a bisector?