D3.js is an HTML/SVG/CSS based data visualization library, which is a very powerful presence in the field.

I will use it in my work in the future, so I started a journey of self-study. During the learning process, I mainly studied through Curran Kelleher’s series of tutorials. This note is used for learning, sorting and sharing, and can be successively studied, updated and recorded in….

The original link

Study Purpose:

  • Be familiar with d3.js
  • Practice drawing with SVG and get familiar with the operation
  • Practice the basic functions and operations of d3.js

Completed effect drawing: online demo

At the beginning of D3 impression

Select set

D3 is to realize data visualization, which still cannot be separated from the selection and operation of traditional DOM. Therefore, D3 provides DOM manipulation instructions similar to Jquery:

  • d3.select: selects the first specified element
  • d3.selectAll: Selects all elements
const svg = d3.select('svg') / / select SVG
const p = svg.selectAll('p') // Select all p tags in SVG

Copy the code

Of course, you can use #id and.class to select the ID and class

Check the status

D3. select and d3.selectAll return the selection set. Add, delete, and modify the selection set. There are three functions to check the status:

  • selection.empty()Returns true if set is empty, false otherwise
  • selection.node()Returns the first non-empty element, or NULL if the selection set is empty
  • selection.size()Returns the number of elements in the selection set

Sets and gets properties

After selecting with Select or selectAll, attributes can be obtained and set via attr, and elements can be added using the Append method

const svg = select('svg');
svg.append('circle')
    .attr('r'.'30')

Copy the code

Use D3 and SVG for drawing

The HTML file

<html lang="en">
<head>
    <title>Smile face with d3</title>
</head>
<body>
    <svg width="960" height="500"></svg>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="index.js"></script>
</body>
</html>
Copy the code

The main content is achieved through index.js:

Step 1: Select SVG with D3 and usecircleBuilding outline

const svg = d3.select('svg');
const height = +svg.attr('height');
const width = +svg.attr('width');
svg.append('circle')
    .attr('r',height / 2)
    .attr('cx', width / 2)
    .attr('cy', height / 2)
    .attr('fill'.'yellow')
    .attr('stroke'.'black')

const leftEye = svg.append('circle')
    .attr('r'.30)
    .attr('cx', width / 2 - 100)
    .attr('cy', height / 2 - 80)
    .attr('fill'.'black')

const rightEye = svg.append('circle')
    .attr('r'.30)
    .attr('cx', width / 2 + 100)
    .attr('cy', height / 2 - 80)
    .attr('fill'.'black')
Copy the code

Key points:

  • throughattrThe property that gets isstringType, passparseFloator+convertnumbertype
  • The circularcircleThe properties of thecx cyAnd so on, using a variable as the value of its size setting, rather than a number, to facilitate future maintenance
  • LeftEye and Righteous create it in a similar way and put the eye in the right place.

Code optimization

1) It can be found that the operation of Cx and CY on the center of the three circles appears several times, but the function is only to place the circle at the center. Therefore, a one-time operation can be achieved by grouping with

in SVG.

const g = svg.append('g')
    .attr('transform'.`translate(${ width / 2}, ${ height / 2}) `)

// This way, when drawing a circle, you can remove the operation on the center of the circle, because the default position is in the center
const circle = g.append('circle')
    .attr('r',height / 2)
    .attr('fill'.'yellow')
    .attr('stroke'.'black')
Copy the code

2) In the same way, the eye operation is also a bit tedious, so you can improve the maintainability of the code by using variables and grouping.

const eyeSpacing = 100;
const eyeYoffset = - 80.
const eyeRadius = 30;

const eyesG = g.append('g')
    .attr('transform'.`translate(0, ${eyeYoffset}) `);

const leftEye = eyesG.append('circle')
    .attr('r', eyeRadius)
    .attr('cx', - eyeSpacing)

const rightEye = eyesG.append('circle')
    .attr('r', eyeRadius)
    .attr('cx', + eyeSpacing)
Copy the code

Step 2 Mouth

The mouth is actually an arc, drawn using path in SVG. If you are familiar with path, you can draw the parameters directly, but D3 has better support for arcs. You can use the d3.arc function to draw arcs conveniently.

The arc function

According to the official API manual, functions are used as follows:

var arc = d3.arc();

arc({
  innerRadius: 0.outerRadius: 100.startAngle: 0.endAngle: Math.PI / 2
}); / / "M0, - 100 a100, 100,0,0,1,100,0 L0, 0 z"
Copy the code

InnerRadius is the radius of the inner circle, outerRadius is the radius of the outer circle, startAngle and endAngle are the starting and ending radians respectively (a complete circle is 0 to 2PI).

Realization of smiley face

Based on the above, the code is as follows:

const mouth = g.append('path')
    .attr('d',d3.arc()({
        innerRadius: 150.outerRadius: 170.startAngle: Math.PI /2.endAngle: Math.PI * 3 / 2
    }))
Copy the code

His eyebrows

The eyebrows are replaced with a simple rectangle, namely

in SVG. Using the same programming style as above, the eyebrows are grouped into a group and the position is set.

const eyebrowWidth = 50;
const eyebrowHeight = 10;
const eyebrowYoffset = -150;
const BrowG = g.append('g')
    .attr('transform',`translate(${-eyebrowWidth / 2},${eyebrowYoffset})`);

const leftEyebrow = BrowG.append('rect')
    .attr('width',eyebrowWidth)
    .attr('height',eyebrowHeight)
    .attr('x',-eyeSpacing)

const rightEyebrow = BrowG.append('rect')
    .attr('width', eyebrowWidth)
    .attr('height', eyebrowHeight)
    .attr('x', eyeSpacing)
Copy the code

Add animation

Use the transition function to animate your eyebrows to move your smile

const BrowG = g.append('g')
    .attr('transform',`translate(${-eyebrowWidth / 2},${eyebrowYoffset})`);
BrowG.transition().duration(2000)
    .attr('transform', `translate(${-eyebrowWidth / 2},${eyebrowYoffset - 50})`)
    .transition().duration(2000)
    .attr('transform', `translate(${-eyebrowWidth / 2},${eyebrowYoffset})`).duration(2000)

Copy the code

Note:

  • transitionThe function has to be evaluated, that is, if you add phi to phi<g>On, the corresponding added property animation should be intransformOn; If the animation is in<rect>On, the animation should be inyOn the property.
  • Add animation cannot be inappendFunction after the concatenation, for exampleBrowG.append('g').attr(...) .transition()No. Because over animation cannot bind inappendOn the element of the.

The complete code

code downlaod here

const svg = d3.select('svg');
const height = +svg.attr('height');
const width = +svg.attr('width');
const g = svg.append('g')
    .attr('transform',`translate(${ width / 2}, ${ height / 2})`)

const circle = g.append('circle')
    .attr('r',height / 2)
    .attr('fill', 'yellow')
    .attr('stroke','black')

const eyeSpacing = 100;
const eyeYoffset = -80
const eyeRadius = 30;
const eyebrowWidth = 50;
const eyebrowHeight = 10;
const eyebrowYoffset = -150;

const eyesG = g.append('g')
    .attr('transform', `translate(0, ${eyeYoffset})`);

const leftEye = eyesG.append('circle')
    .attr('r', eyeRadius)
    .attr('cx', - eyeSpacing)

const rightEye = eyesG.append('circle')
    .attr('r', eyeRadius)
    .attr('cx', + eyeSpacing)

const mouth = g.append('path')
    .attr('d',d3.arc()({
        innerRadius: 150,
        outerRadius: 170,
        startAngle: Math.PI /2,
        endAngle: Math.PI * 3 / 2
    }))

const BrowG = g.append('g')
    .attr('transform',`translate(${-eyebrowWidth / 2},${eyebrowYoffset})`);
BrowG.transition().duration(2000)
    .attr('transform', `translate(${-eyebrowWidth / 2},${eyebrowYoffset - 50})`)
    .transition().duration(2000)
    .attr('transform', `translate(${-eyebrowWidth / 2},${eyebrowYoffset})`).duration(2000)

const leftEyebrow = BrowG.append('rect')
    .attr('width',eyebrowWidth)
    .attr('height',eyebrowHeight)
    .attr('x',-eyeSpacing)

const rightEyebrow = BrowG.append('rect')
    .attr('width', eyebrowWidth)
    .attr('height', eyebrowHeight)
    .attr('x', eyeSpacing)


Copy the code