knowledge
Axes and scales
scale
continuity
scaleLinear
The actual data is mapped to screen space
Var xScale wasn't entirely = d3. ScaleLinear (.) domain ([0, d3. Max (dataset)]). The range ([0250])Copy the code
- Domain corresponds to the original data, that is, the data to be analyzed
- Range () corresponds to the data displayed on the screen, known as pixels
- Ticks () is used to set the number of ticks on the whole coordinate axis instead of the interval between ticks. The text displayed on the ticks is the domain() value of the scale
- TickSize () displays the grid lines so that they cover the entire display area and can be drawn as grid lines with Ticks. Transparency options are also provided in the reference case to prevent grid lines from affecting the presentation of actual data
- Nice () optimizes the domain, usually plus
/ / scale
let x = d3.scaleLinear().range([0, width])
let xScale = x.domain([0.10])
/ / the x axis
let xAxis = svg.append('g')
.attr('class'.'xAxis') .attr('transform'.`translate(0, ${height}) `) .call(d3.axisBottom(xScale)) Copy the code
Use d3.axistop and d3.axisBottom() to control how the scale is displayed above or below the coordinate axis,
Note: If the X-axis is to be set at the bottom, d3.axisBottom() also needs the transform to be shifted to the canvas position (0,trueHeight)
scaleTime
D3.scaletime () is similar to d3.scalelinear () linear scale, except that the input field becomes a timeline.
let scale = d3.scaleTime()
.domain([new Date(2017, 0, 1, 0), new Date(2017, 0, 1, 2)])
Range ([0100])Copy the code
Time scales are more commonly used for data that changes over time. Another d3.scaleuTC () is calculated according to Universal Standard Time (UTC)
Sometimes the time is an array and it’s not contiguous if you just use the time scale you’ll see a lot of points in time that you don’t want to use on the coordinate axis, you can use scaleBand() after you’ve converted the time format to a string
let x = d3.scaleTime().range([0, width])
let xScale = x.domain([new Date(2017.1), new Date(2017.6)])
/ / the x axis
let xAxis = svg.append('g')
.attr('class'.'xAxis')
.attr('transform'.`translate(0, ${height}) `) .call(d3.axisBottom(xScale)) Copy the code
There is a plugin for handling time formats called moment.js
scaleQuantize
The quantized scale d3. ScaleQuantize () is also a continuous scale. The definition domain is continuous, while the output domain is discrete.
let scale = d3.scaleQuantize().domain([0.10]).range(["small"."medium"."long"]
Copy the code
discontinuity
The origin to
scaleOrdinal
Both the input and output fields of d3.scaleordinal () use discrete data.
let scale = d3.scaleOrdinal().domain(['jack'.'rose'.'john']).range([10, 20, 30])
Copy the code
It can also be used to fill in color scales, using some of the color schemes that come with D3
d3.scaleOrdinal(d3.schemeCategory10)
Copy the code
Domain () and range() correspond one to one. What if the values are different?
domain()
The values of correspond in a sequential looprange()
The value of the
let height = 400
let width = 600
let x = d3.scaleOrdinal().range([150.300.450.600])
let xScale = x.domain(['Beijing'.'Shanghai'.'guangzhou'.'shenzhen'])
/ / the x axis
let xAxis = svg.append('g') .attr('transform'.`translate(0, ${height}) `) .call(d3.axisBottom(xScale)) Copy the code
Non-origin start
scaleBand
- domain
- Range: Sets the output range
- Round: indicates whether to round
- RangeRound: Integrate range and round
- PaddingInner: set paddingInner [0,1]
- PaddingOuter: set paddingOuter [0,1]
- PaddingInner: paddingOuter
- Align: set scale position default 0.5 range [0,1]
- “Bandwidth” : obtains “bandwidth”
- Step: Getting the step simply refines the API; Padding, align feel good
. The rest of the samelet x = d3.scaleBand().range([0, width])
Copy the code
Basically, a bar chart will use this x axis.
axis.ticks()
Getting an array of values from one value to another divided into a specific number returns an array of nearly count+1 values between start and stop (including stop) that is uniformly spaced and nicely rounded. Each value is 1,2 or 5 times a power of 10 (10x).
Scenario: When creating a y-coordinate scale, maybe you want your y-coordinate scale to show only two, i.e. only the end, what if? ticks(1)
Notice the section of ticks, and the value of ticks is not determined by the number of ticks at all, but by the divisor. First, think about whether the divisor is a multiple of 1,2,5 and 10 in your mind. (if not, don’t worry, you won’t get the effect you want.)
https://www.tangshuang.net/3270.html
The path
attribute
-
d
-
- M = moveto(M X,Y) : Moves the brush to the specified coordinate position
- L = lineto(L X,Y) : draw a lineto the specified coordinate position
- Horizontal lineto H = horizontal lineto(H X) : draws a horizontal lineto the specified X position
- V = vertical lineto(V Y) : draw a vertical lineto the specified Y coordinate
- C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY) : Cubic Bezier curve
- S = smooth curveto(S X2,Y2,ENDX,ENDY) : smooth curvature
- Quadratic Belzier curve(Q X,Y,ENDX,ENDY) : quadratic Bessey curve
- T = smooth quadratic Belzier curveto(T ENDX,ENDY) : mapping
- A = elliptical Arc (A RX and RY, XROTATION, FLAG1, FLAG2, X, Y) : Arc
- Z = closepath() : Close the path All the preceding commands allow lowercase letters. Upper case indicates absolute location, lower case indicates relative location
-
Fill the color
-
Stroke color
-
- The stroke-Linecap property lets you control the shape at the end of the line. You can choose butt, square and round.
-
- The stroke-linejoin property is similar, but it controls the connection between two line segments. You can use it when drawing polylines. It has three values, miter, round, and bevel
- Stroke-width: width of the stroke
- Transform =”translate(x,y)”: transform (x =stroke-width/2, y=stroke-width/2)
Straight line command
<path d="M10 20 H90 V90 H10 V20"></path>
Copy the code
This can be simplified by using the closed path command Z instead of the last stroke pointing to the origin
<path d="M10 20 H90 V90 H10 Z"></path>
Copy the code
Line versus command (lowercase)
Relative commands use lowercase letters, and instead of specifying an explicit coordinate, their arguments indicate how far to move relative to the point in front of them.
<path d="M100 20 h 50 v 50 h -50 Z"></path>
Copy the code
D3 Path Generator
https://github.com/d3/d3-shape/tree/v1.3.7
D3. Line (…). X (…). Y (…). Used for line charts
D3.geopath ().projection() is used for maps
D3.area () is used for the subject river
D3. Arc (…). InnerRadius (…). OuterRadius (…). Used for the pie chart
D3. LineRadial (). Angle (…). The radius (…). Polar version of d3.line(…)
Data binding Data Join
D3.js binds three states of data
The binding element
-
- Datum (data) binds a data STR to all selection sets
p.datum(str).text((value,index) = >{return `${index} is ${value}` })
Copy the code
-
- Data (data) Binds a group of data, whose dataset is an array
p.data(dataset).text((el,i) = >{return el})
Copy the code
- .data (…). Used to bind a batch of primitives to a batch of data
- Datum (…). Used to bind a data to a specific primitive
-
Mismatch between the number of data sets and the number of selected setsupdate/enter/exit
Enter: add enough rect elements (number of matching data sets)
g.selectAll('rect').data(dataset).enter().append('rect')
Copy the code
-
Insert elements
-
- Append (EL) : Inserts an element at the end of the selection set
- Insert (EL) : Inserts elements in front of the selection set
-
Remove element remove()
A concise form of data-join
The initial primitive property of the Enter data must be set, and update will reset the initial value each time, causing the animation to look weird
d3.selectAll('rect').data(data2).join('rect').transition().duration(1500).attr('width',d=>xScale(d.value))
Copy the code
Still support customization
.join(
enter= > enter.append("text").attr("fill"."green").text(d= > d),
update => (),
exit => ()
)
Copy the code
A query
Hierarchical queries are often used in code that configures coordinate axes
D3. selectAll(‘#new rect’) : select rect (‘#new rect’); select rect (‘#new rect’);
attribute
SVG-attribute
Let num = +(‘2222.4’) let num = +(‘2222.4’)
Enter
Application scenario: There is data but no primitives. This parameter is used to bind data for the first time
- Generate corresponding primitives according to the new data
- Generate placeholders for primitives, placeholder contents need to be added by the programmer (append)
Const p = maingroup. SelectAll (' class'). The data (data). Enter (), append ('). Attr (...).Copy the code
The bar graph is implemented with Enter
- Enter essentially generates a pointer to the parent node
- The append operation is equivalent to adding a pointer number of primitives after the parent node and binding it to the extra data, typically with a forEach layer around the data set
// The original implementation
data.forEach(d= > {
g.append('rect')
A. TTR event ('width', xScale(d.value))
.attr('height', yScale.bandwidth())
.attr('fill'.'aqua') .attr('y', yScale(d.name)) .attr('opacity'.'0.5') }) // Implement with Enter g.selectAll('.dataRect').data(data).enter().append('rect') .attr('class'.'dataRect') .attr('width', d => xScale(d.value)) .attr('height',yScale.bandwidth()) .attr('fill'.'green') .attr('y', d => yScale(d.name)) .attr('opacity'.'0.5') Copy the code
Update
Application scenario: There are elements and data
Once the placeholders are filled, the update state is directly entered
Function to set primitive properties
d3.selectAll('rect').data(dataset).attr('width',(d,i)=>{return xScale(d.value)})
Copy the code
Set the values of the horizontal axis in all recT labels by the function. Note that the dataset format must be the same as the original dataset format
.data (data, keyFunction)
The default data function uses index as index. If the given data index changes (i.e., the data order changes or the amount of data increases or decreases), the content binding order will be out of order.
Bind data to primitives with key, and keyFunction executes once for each input bound data and for each containing data primitives
d3.selectAll('rect').data(dataset, d => d.name)...
Copy the code
KeyFunction will report an error if the primitive has not been bound to any data before
- The first binding is based on the index
- In actual tasks, elements are dynamically added and deleted based on the “bar” tree of data. You only need to specify the DOM ID during the addition
Update, the most common state for practical visualization tasks, is often wrapped as a separate function
UpdateSelection. Merge (enterSelection). Attr (...). Attr (...).Copy the code
- Combine the two Selection operations together
- EnterSelection needs to be called at least before merging with updateSelection
Append (...).
Statement to add good primitives
Update is combined with animation
The transition (…). After the call, subsequent chain calls become numeric gradients, varying by.duration(…) set
d3.selectAll('rect').data(data2,d => d.name).transition().duration(1500).attr('width',d=>xScale(d.value))
Copy the code
Exit
Application scenario: There are elements but no data
Remove primitives without data
.selectAll('.class').data(data).exit().remove()
Copy the code
The data processing
The picture related
Add images
-
A graph itself is given in link form
-
Image itself is also an HTML element
-
Herf represents the path of the image
-
ViewBox: preserveAspectRatio Picture alignment and scaling Optional parameters: meet: retain the complete picture slice: adapt to the ViewBox. It’s essentially modifying the viewbox property.
-
none
xMidYMid slice
Center sectionxMidYMid meet
Zoom out to keep the full content centered in the viewable boxxMinYMin slice
Slice leftxMaxYMax meet
Zoom out to keep the full content displayed at the bottom of the viewable box
svg.append('image').attr('id'.'img')
.attr('x'.400).attr('y'.300)
.attr('preserveAspectRatio'.'none')
.attr('href'.'sss.jpg')
Copy the code
Image texture
<patterns>
Copy the code
pattern.Unit
Insert the texture. Js
The data set
CSV data
Essential text, no format
Then (…); then(…); The callback function in
D3. CSV (' path/to/data. The CSV). Then (data= >{... })Copy the code
The return value of.csv is a Promise object
The JSON data
Map data
TopoJson
It’s essentially a JSON format
The feature of GeoJson data redundancy is processed to save storage space
Developed by D3 author Mike Bostock
GeoJson
It’s essentially a JSON format
D3.js geoPath uses GeoJson lattice like map data
JSON reading
TopoJson needs to be converted to GeoJson
<script src="./source/topojson.js"></script>
// The second argument represents the object to which the data is specifically directed
worldmeta = topojson.feature(data, data.objects.countries);
Copy the code
D3 Event Mechanism
On (event type, trigger action)
D3. Select ('# someprimitive ') on (' click ', myCallBack)
Copy the code
D3-tip
Automatically displays the dialog box in the appropriate place
Initialize the
<script src="./source/d3-tip.js"></script>
const tip = d3.tip()
.attr('class'.'d3-tip').html(function (d) {
return d.properties.name
});
svg.call(tip); Copy the code
Hierarchical visualization
Hierarchy: Tree, Tree, and Hierarchy
d3.hierarchy(data)
Hierarchies the JSON data, noting that the height parameter represents the value in terms of the current node’s children
Direct visualization solutions
d3.tree
More intuitive visualization scheme
d3.partition & d3.arc for 'd' of <path>
steps
- with
d3.hierarchy(data)
Hierarchical json data - with
d3.tree
The hierarchy is spatially divided on the canvas (like a scale), adding x, y to each node
Related parameters
Node.descendants () is used to generate descendant arrays and return all other child nodes, including root, as a list flattened by breadth-first traversal
Visualization of network structure
Interactive API
Drag
D3.drag () General definition Three functions define the start operation, the action in the interaction, and the end operation respectively
- dragstarted
simulation.stop()
- Dragged is updating the coordinates over here
ticked
- dragended
simulation.restart()
Asynchronous mechanisms
implementation
Initial coordinate axis
const svg = d3.select('#main')
// + string to number
const width = +svg.attr('width')
const height = +svg.attr('height')
const trueWidth = width - margin.left - margin.right
const trueHeight = height - margin.bottom - margin.top // The starting coordinates of the grouping need to translate to the starting position of the real canvas const g = svg.append('g').attr('id'.'mGroup').attr('transform'.`translate(${margin.left}.${margin.top}) `) const xScale = d3.scaleLinear() .domain(d3.extent(data, d => d.value)).range([0, trueWidth]) // Domain optimization .nice() const xAxis = d3.axisBottom(xScale) / / grid lines .tickSize(-trueHeight) // Add the axes to the canvas g.append('g').call(xAxis) // Move the x axis to the bottom of the canvas .attr("transform".`translate(0,${trueHeight}) `) const yScale = d3.scaleBand() .domain(data.map(d= > d.name)).range([0, trueHeight]) // Keep each recT of the bar chart 0.2 void .padding(0.2) const yAxis = d3.axisLeft(yScale).tickSize(-trueWidth) g.append('g').call(yAxis) Copy the code
A histogram
Implement horizontal bar chart
- The forEach implementation
data.forEach(d= > {
g.append('rect')
.attr('width', xScale(d.value))
.attr('height', yScale.bandwidth())
.attr('fill'.'aqua')
.attr('y', yScale(d.name)) }) Copy the code
- Enter to realize
g.selectAll('.dataRect').data(data).enter().append('rect')
.attr('width', d => xScale(d.value))
.attr('height', yScale.bandwidth())
.attr('fill'.'green')
.attr('y', d => yScale(d.name))
Copy the code
Gradient effect implementation
// Linear gradient
const linearGradient = svg.append("defs")
.append("linearGradient")
.attr("id"."gra")
linearGradient.append('stop')
.attr('offset'."5%") .attr('stop-color'."gold") linearGradient.append('stop') .attr('offset'."95%") .attr('stop-color'."red") // Fill the rect fill .attr('fill'.'url(#gra)') Copy the code
Dynamic scatter diagram
- You need to re-const AxisGroup based on the axes, and you need to do a render remove
xScale = d3.scaleLinear()
.domain(d3.extent(data, xValue))
.range([0, trueWidth])
.nice();
const xAxis = d3.axisBottom(xScale) // The grid should be in xAxis, without remove .tickSize(-trueHeight) .tickPadding(10); let xAxisGroup = g.append('g').call(xAxis) .attr('transform'.`translate(The ${0}.${trueHeight}) `) .attr('id'.'xaxis') xAxisGroup.append('text') .attr('font-size'.'2em') .attr('y'.60) .attr('x', trueWidth / 2) .attr('fill'.'# 333333') .text(xAxisLabel); xAxisGroup.selectAll('.domain').remove(); Copy the code
- Scatter rendering and text matching are performed in updates, using timers to timer the refresh view
/ / scatter
let circleUpdates = g.selectAll('circle').data(seq, d => d['region'])
// Enter () adds a sufficient number of circles
let circleEnter = circleUpdates.enter().append('circle')
.attr('transform'.`translate(${margin.left}.${margin.top}) `)
// xScale must be wrapped around the data to the corresponding pixel, first value xValue and then call mapping to coordinates .attr('cx', d => xScale(xValue(d))) .attr('cy', d => yScale(yValue(d))) .attr('r'.15) .attr('fill', d => color[d['region']]) .attr('opacity'.0.5) .text(d= > d['region']) // Add the city name for the scatter let textUpdates = g.selectAll('.province_text').data(seq); let textEnter = textUpdates.enter().append('text') .attr('transform'.` ` translate (120, 40)) .attr("class"."province_text") .attr("x", (datum) => { return xScale(xValue(datum)); }) .attr("y", (datum) => { return yScale(yValue(datum)); }) .attr("dy"."1em") // Use the middle value of the text as the anchor point .style("text-anchor"."middle") .attr("fill"."# 333333") .text(function (d, i) { return d['region']; }); // console.log(textEnter.text); textUpdates.merge(textEnter).transition().ease(d3.easeLinear).duration(duration) .attr('x', (datum) => { return xScale(xValue(datum)); }) .attr('y', (datum) => { return yScale(yValue(datum)); }); // How should easeLinear change with each update circleUpdates.merge(circleEnter).transition().ease(d3.easeLinear).duration(duration) .attr('cx', d => xScale(xValue(d))) .attr('cy', d => yScale(yValue(d))) Copy the code
- Date update
// Delete the old date
g.selectAll('.date_text').remove();
g.append("text")
.data(['seq'])
.attr('class'.'date_text')
.attr("x", margin.left) .attr("y", margin.top) // The y offset distance .attr("dy"."1em") .attr("fill"."#504f4f") .attr('font-size'.'3em') .attr('font-weight'.'bold') .attr('opacity'.0.7) .text(time); Copy the code
- D3.scv () call data
d3.csv('./data/hubeinxt.csv').then(data= > {
data = data.filter(d= > {
return d['region']! = ='total'
})
// Convert the number from a numeric number to a string
data.forEach(d= > { d['Number of confirmed cases'] = +(d['Number of confirmed cases']) d['Newly confirmed'] = +(d['Newly confirmed']) }) // Extract a sequential date that does not repeat the date allDates = [...new Set(data.map(d= > d['date']))].sort((a, b) = > new Date(a) - new Date(b)) sequential = [] // Sequential data sequential[0]={{Date: "2020/1/21", region: "Ezhou ", Number of confirmed cases: 0... },{Date: "2020/1/21", Region: "Enshi Prefecture ", Number of confirmed cases: 0... },... } allDates.forEach(d= > { sequential.push([]) }) data.forEach(d= > { sequential[allDates.indexOf(d['date'])].push(d) }) console.log(sequential) // Initialize render renderInit(data) let c = 0/ / counter // Create an animation loop and re-call interval every duration let interval = setInterval((a)= > { // Determine if all data has been rendered if (c >= allDates.length) { clearInterval(interval) } else { Update dynamically updates coordinate points every duration if data is not completely updated renderUpdate(sequential[c]) c += 1 } }, duration); }) Copy the code
Dynamic line chart
A single primitive implementation is bound to a single data
- Append polyline in renderInit function
g.append('path').attr('id'.'alterPath');
Copy the code
- Select the dataset in updateAlter and set its properties
d3.line()
Is an interface to the d property that converts bound data directly into a path,.x() .y()
The data set array is passed ind3.line()
Is automatically split into rows (i.e., a line corresponds to a row of data).curve()
Interpolation strategy, use what way to insert the data (there are various ways to introduce) https://github.com/d3/d3-shape/blob/v1.3.7/README.md#curves
const line = d3.line()
.x(d= > d['date'])
.y(d= > d['Number of confirmed cases'])
//.curve(d3.curveBasis)
// Cardinal mode ensures that certain data points are passed
.curve(d3.curveCardinal.tension(0.5)) Copy the code
- Polyline setting property
d3.select('#alterPath').datum(data)
.attr('class'.'datacurve')
// Fill does not set "None", the path will automatically close, a black area
.attr("fill"."none")
.attr("stroke"."green")
.attr("stroke-width".2.5) .transition().duration(2000) .attr("d", line) Copy the code
Stack Stack
Determine the maximum stack height in the Y coordinate
const mStack = d3.stack()
.keys(naiveKeys)
.order(d3.stackOrderNone)(naiveData)
MStack [I][j][1
const yMaxValue = d3.max(mStack, d => d3.max(d, sub => sub[1]))
const yScale = d3.scaleLinear().range([0, innerHeight].reverse()) .domain([0, yMaxValue]).nice() const yAxis = d3.axisLeft(yScale).tickSize(-innerWidth); const yAxisGroup = g.append('g').attr('class'.'yAxis').call(yAxis); Copy the code
.keys(naiveKeys)
Set which properties to stack;
The array we passed in here has four elements, so stack four
-
.order(d3.stackOrderNone) In what order these attributes should be stacked
-
- d3.stackOrderNone
- D3. stackOrderAscending puts the largest summation on top
- D4. StackOrderDesending has the smallest sum at the top
-
.offset(d3.stackoffsetwiggle) undulating optimization, used in theme rivers
Data discrete mapping (mapping apples to red)
const color = d3.scaleOrdinal()
.domain(naiveKeys)
ScaleOrdinal can directly match the color array and data
.range(d3.schemeSet3)
Copy the code
D3 comes with a color scheme
https://github.com/d3/d3-scale-chromatic
Data-join of stack Data
Each data is bound to an array of length 2;
- The two numbers represent the interval in the stacked data
- The data property of an array can be mapped to the original data
Set the scale, position, and height with stacked data
Note: If possible, call the scale mapping data first and then do the operation! (Green box)
Data – join nesting
Layer 1, join a group of groups.
g.selectAll('.datagroup').data(naiveStack).join('g').attr('class'.'datagroup').attr('fill', d => color(d.key))
Copy the code
The second layer is nested with selectAll, which performs a progressive split of one of the outer arrays from one of the above datagroups.
And when the second layer opens the array, the arguments to the data() function receive the results of the previous layer
After the above...// This side handles each small rectangle
.selectAll('.datarect').data(d= > d).join('rect').attr('class'.'datarect')
.attr('y', d => yScale(d[1]))
// You need to access x through data in a small array
.attr('x', d => xScale(xValue(d.data)))
.attr('height', d => yScale(d[0]) - yScale(d[1])) .attr('width', xScale.bandwidth()); Copy the code
Map rendering
- Convert the data to GeoJson format
d3.json('./data/countries-110m.json').then(data= > {
worldmeta = topojson.feature(data, data.objects.countries);
}
Copy the code
- Select the projection mode
https://github.com/d3/d3-geo for more different projections;
//const projection = d3.geoMercator();
//const projection = d3.geoOrthographic();
//const projection = d3.geoStereographic();
//const projection = d3.geoEquirectangular();
// Various projection methods const projection = d3.geoNaturalEarth1(); //const projection = d3.geoTransverseMercator(); // Tell GeoPath how to project data onto the screen const pathGenerator = d3.geoPath().projection(projection); Copy the code
- GeoPath receives the format features attributes in WorldMeta
d3.json('./data/countries-110m.json').then(data= > {
. const paths = g.selectAll('path')
.data(worldmeta.features, d => d.properties.name)
.enter().append('path')
} Copy the code
- Screen adaptation of the displayed map, call interface, no need to manually adjust the scale of the first parameter can be spread canvas size
d3.json('./data/countries-110m.json').then(data= > {
. projection.fitSize([innerWidth, innerHeight], worldmeta);
}
Copy the code
The tree
Visualize with reference to hierarchy
- Get the data and make a hierarchy of the data, and make a spatial division of the hierarchy
d3.json('./data/games.json').then(data= > {
console.log(data);
// Hierarchy the data
root = d3.hierarchy(data)
// Map the hierarchy to the canvas
root = d3.tree().size([innerWidth, innerHeight])(root) render(root) }) Copy the code
- In the render function, the hierarchy is wired with link
g.selectAll('path').data(data.links()).join('path')
.attr('fill'.'none')
.attr('stroke'.'black')
.attr('opacity'.0.4)
//x(d=>d.y) swaps coordinates to display a tree from left to right, otherwise the default display is from top to bottom tree
.attr('d', d3.linkHorizontal().x(d= > d.y).y(d= > d.x)) Copy the code
- Assign a color to the node, and fill specifies that the child node color follows that of the second-tier node
const fill = d= > {
if (d.depth === 0)
return color(d.data.name)
// If it is a node after the second layer, look for the color of its parent node until the second layer is found
while (d.depth > 1)
d = d.parent; return color(d.data.name); } g.selectAll('g').data(data.descendants()).join('circle') .attr('cx', d => d.y) .attr('cy', d => d.x) .attr('r'.6) // Node color the second layer is a color, and its children follow its color .attr('fill', fill) .attr('stroke-width'.3) Copy the code
icicle
On the basis of the tree view, change it to partition
D3. partition create new partition layout (sunrise and icicle)
d3.json('./data/games.json').then(data => {
root = d3.partition().size([height, width])(
// tell D3 the value of the non-leaf node to calculate the sum of all its leaf node values d3.hierarchy(data).sum(d => d.popularity)
.sort((a, b) => b.popularity - a.popularity)
) render(root) }) Copy the code
To handle flat array conversion RECT, use join mode
g.selectAll('.dataRect').data(data.descendants()).join('rect')
.attr('class'.'dataRect')
Copy the code
The pie chart
The d3.arc() interface is required, and the method generates an arc. You need to set the inside radius and the outside radius for the arc. If the inner radius is 0, the result will be a pie chart, otherwise the result will be a ring chart
The d3.pie() method is used to generate the pie chart. It takes data from the data set and calculates the start and end angles of each wedge of the pie chart.
const g = svg.append('g').attr('id'.'maingroup')
.attr('transform', `translate(${width/2}.${height/2}) `);Copy the code
sunburst
- Wrap the Icicle diagram around a circle
// The rising sun map changes size to a circle
root = d3.partition().size([2 * Math.PI, height / 1.6]) ( // tell D3 the value of the non-leaf node to calculate the sum of all its leaf node values
d3.hierarchy(data).sum(d= > d.popularity)
.sort((a, b) = > b.popularity - a.popularity)
) Copy the code
- Add path, fill color circle arc of optional parameters: https://zhuyali.github.io/d3-doc/d3-shape/arc.html#arcpadangle – function
const arc = d3.arc()
// Start Angle, clockwise
.startAngle(d= > d.x0)
.endAngle(d= > d.x1)
/ / within a radius
.innerRadius(d= > d.y0) / / outer radius .outerRadius(d= > d.y1) .padAngle(d= > Math.min((d.x1 - d.x0) / 2.0.005)) g.selectAll('.dataPath').data(filterData).join('path') .attr('class'.'dataPath') .attr('fill', fill) .attr('d', arc) Copy the code
- After filtering the root node, the center of the circle is blank and can be turned into a ring
const filterData = data.descendants().filter(d => d.depth ! = = 0)Copy the code
- The transform is applied from left to right
g.selectAll('.dataText').data(filterData).join('text')
.attr('text-anchor'.'middle')
.attr('font-size', d => d.data.name.length > 13 ? '0.8 em' : '0.9 em')
.attr("transform".function (d) {
const x = (d.x0 + d.x1) / 2 * 180 / Math.PI;
const y = (d.y0 + d.y1) / 2; // note that there is an implicit transform inherited from the maingroup; return `rotate(${x - 90}) translate(${y},0) rotate(${x < 180? 0 : 180}) translate(0, 5)`; }) .text(d= > d.data.name) Copy the code
This article is formatted using MDNICE