preface

In the process of website page loading and form submission, progress bars are often used to express the loading process to optimize user experience. Common progress bars include rectangular progress bars and circular progress bars, as shown below:

We often use SVG or Canvas to draw dynamic graphics, but the drawing process is relatively tedious. For the intuitive and beautiful progress bar, the community also provides mature solutions such as Highcharts /ECharts and so on, but the configuration-based development method ultimately cannot achieve 100% custom drawing. This article takes you step by step to implement a dynamic progress bar using D3.js and shares the logic behind the code.

Basic requirements

  • Know how to draw basic graphics in SVG
  • Learn about d3.js V4
  • Know how to draw basic graphics in SVG using D3.js (V4)

Draws a circular progress bar

For a circular progress bar, we first split its tasks:

  • Draws nested arcs
  • Real-time data display at the center of the circle
  • Show the animation
  • beautify

1. Draw nested arcs

For circles, SVG provides ready-made circle tags, but the disadvantage is that circles are fine for circular progress bars, but tricky for further extensions such as drawing semicircles. D3.js provides an ARC API to encapsulate the circle drawing method:

var arc = d3.arc()
            .innerRadius(180)
            .outerRadius(240)
            //.startAngle(0)
            //.endAngle(Math.PI)

arc(); / / "M0, - 100 a100, 100,0,0,1,100,0 L0, 0 z"
Copy the code

The above code implements the drawing logic for two nested circles. D3.arc () returns an arc constructor and sets the radius of the inner and outer circles, and the starting and ending angles via a chain call. Execute the arc() constructor to get the path data to bind to . The complete code is as follows:

<! --html-->
<svg width="960" height="500"></svg>

<script>
    var arcGenerator = d3.arc().innerRadius(80).outerRadius(100).startAngle(0);
    
    var picture = d3.select('svg').append('g').attr('transform'.'translate(480,250)');
</script>
Copy the code

The above code implements two steps:

  • 1. Generate an arc constructor with 0 degrees as the starting pointarcGenerator
  • Set 2.transformGraph offset so that the graph is in the center of the canvas

There are no elements on the canvas yet, so let’s draw the actual graphics.

var backGround = picture.append("path")
        .datum({endAngle: 2 * Math.PI})
        .style("fill"."#FDF5E6")
        .attr("d", arcGenerator);
Copy the code

We add a element to the canvas picture, bind {endAngle: math.pi} to the element using the datum() method based on the endAngle() property, and assign the arc constructor to the path path D. This produces an arc with the specified background color, which looks like this:

Once the first arc is drawn, the progress bar is essentially a second arc on top of the first, according to the SVG hierarchy z-Index. In the same way:

var upperGround = picture.append('path')
        .datum({endAngle:Math.PI / 2})
        .style('fill'.'#FFC125')
        .attr('d',arcGenerator)
Copy the code

After the code is run, we can get:

2. Real-time data display at the center of the circle

In the first part we implemented nested circles based on two paths. In the second part, we will realize the real-time data display at the center of the circle. When the progress bar is loaded, we add data in the center of the circle to express the current loading progress, using the

tag to display:

var dataText = g.append('text')
        .text(12)
        .attr('text-anchor'.'middle')
        .attr('dominant-baseline'.'middle')
        .attr('font-size'.'38px')
Copy the code

Temporarily set the data to 12 and set the horizontal center and vertical center to look like this:

3. Animate

We already know from the 1,2 parts:

  • The essence of drawing a progress bar is to change the Angle of the upper arc
  • When the arc is2 PI.PI is a full circle, and PI radians are PIPI.As a semicircle
  • The data in the circle is the current radian relative2 PI.The percentage of

So all we have to do is change the radian value and the value and set how long it takes to do that and we can animate it. In the official example provided by ECharts, setInterval is used to implement data update at fixed intervals. In fact, d3.js also provides a similar method to implement setInterval function:

d3.interval(function(){
    foreground.transition().duration(750).attrTween('d'.function(d){
        var compute = d3.interpolate(d.endAngle,Math.random() * Math.PI * 2);
        return function(t){
            d.endAngle = compute(t);
            returnarcGenerator(d); }})},1000)
Copy the code

Unpack this code:

  • d3.interval()Method providessetInterval()The function of the
  • selection.transition.duration()Sets the time, in milliseconds, required for the transition of the current DOM property to the specified DOM property
  • transation.attrTweenIs the interpolation function API, so what is interpolation?

In general, interpolating a function in a given discrete data allows the continuous function to pass through all data points. For example, given a div, if you want to achieve a linear gradient of the background color from red on the left to green on the right, how do you calculate the color values for each region? Only need to:

var compute = d3.interpolate(d3.rgb(255.0.0),d3.rgb(0.255.0));
Copy the code

Compute is an interpolation function in the range [0,1]. Compute returns the corresponding color value as long as you enter the number in the range. What is the use of interpolation like this? See below:

If the width of div in the figure above is 100, then convert [0,100] into the range data of [0,10] and input it into the function to obtain the corresponding color of a region. Of course, we should not use discrete data as input and output for linear area processing, so D3.js provides more convenient linear gradient apid3. linear, etc., which will not be described here.

Interpolate (D.endangle, math.random () * math.pi * 2); The following interpolation range is realized:

["Current Angle value"."Random Angle value"] // express ranges instead of arrays
Copy the code

And then returns a function of t, so what does that function do? The parameter t, similar to d, is an interpolation internally implemented in d3.js, and its range is [0,1]. Parameter t automatically calculates the appropriate interpolation amount within [0,1] according to the duration() set, and returns the interpolation result to achieve linear and smooth transition animation effect.

After the animation loading effect of the scroll bar is completed, we will write the change logic of the real-time data of the center of the circle. As long as the simple assignment is realized, the complete code is as follows:

d3.interval(function(){
        foreground.transition().duration(750).attrTween('d'.function(d){
            var compute = d3.interpolate(d.endAngle,Math.random() * Math.PI * 2);
            return function(t){
                d.endAngle = compute(t);
                var data = d.endAngle / Math.PI / 2 * 100;
                // Set the value
                d3.select('text').text(data.toFixed(0) + The '%');
                // Pass in a new parameter to generate a new arc constructor
                returnarcGenerator(d); }})},2000)
Copy the code

The final effect is as follows:

4. The beautification

In parts 1, 2, and 3, we have implemented the basic style and function of the progress bar, but the style still looks very monotonous. Next, we make a linear gradient for the progress bar. We use the linear interpolation API provided by D3.js:

var colorLinear = d3.scaleLinear().domain([0.100]).range(["#EEE685"."#EE3B3B"]);
Copy the code

ColorLinear is also an interpolation function. If we input values in the interval [0,100], it will return the corresponding values in the interval [“#EEE685”, “#EE3B3B”]. For example, when the progress bar shows “80%” :

var color = colorLinear(80);
//color = "80%
Copy the code

After realizing the color value, we only need to change the original color when the progress bar changes:

d3.interval(function(){
        foreground.transition().duration(750).attrTween('d'.function(d){
            var compute = d3.interpolate(d.endAngle,Math.random() * Math.PI * 2);
            return function(t){
                d.endAngle = compute(t);
                var data = d.endAngle / Math.PI / 2 * 100;
                // Set the value
                d3.select('text').text(data.toFixed(0) + The '%');
                // Pass in a new parameter to generate a new arc constructor
                return arcGenerator(d);
            }
        })
        .styleTween('fill'.function(d){
            return function(t){
                var data = d.endAngle / Math.PI / 2 * 100;
                // Return the corresponding color value of the value
                returncolorLinear(data); }})},2000)
Copy the code

StyleTween, like attrTween, is an interpolation function that implements style changes. Use the form of chain call to set the value and color of progress bar at the same time. The final result is as follows:

To sum up, we have realized the circular progress bar with color changes under different values, which can be often used in alarm, reminder and other business scenarios.

Draws a rectangular progress bar

The rectangular progress bar is much simpler than the circular one. Based on the same interpolation principle, you can smoothly change the length of the rectangle. Directly on the code:

<head>
    <style>
        #slider {
            height: 20px;
            width: 20px;
            background: #2394F5;
            margin: 15px;
        }
    </style>
</head>

<body>
    <div id='slider'></div>
    <script>
        d3.interval(function(a){
            d3.select("#slider").transition()
                .duration(1000)
                .attrTween("width".function(a) {
                    var i = d3.interpolate(20.400);
                    var ci = d3.interpolate('#2394F5'.'#BDF436');
                    var that = this;
                    return function(t) {
                        that.style.width = i(t) + 'px';
                        that.style.background = ci(t);
                    };
                });
        },1500)
    </script>
</body>
Copy the code

The results are as follows:

conclusion

The key point of drawing progress bar based on D3.js is interpolation, so as to make smooth transition of graph correctly. Rectangles and circles are certainly possible if you must use SVG or pure CSS, but the handling of paths and animations, as well as the writing requirements of CSS, are much more complex. We observed that the logic code used to draw the above two progress bars using D3.js was almost completely implemented in JS. Meanwhile, the code quantity could be controlled to about 20 lines and could be encapsulated and reused, which was very refined and had advantages in the development of custom charts.

For the derivative version of the progress bar dashboard chart, compared with the basic progress bar, the scale description and pointer calculation are added, but all changes are the same, as long as you master the principle of interpolation and use, processing similar charts will be handy.