1. 首页

基于 D3.js 绘制动态进度条

前言

在网站页面加载以及表单提交时,常使用进度条表达加载过程来优化用户体验,常见的进度条有矩形进度条和圆形进度条,如下图所示:

基于 D3.js 绘制动态进度条

我们经常使用 svg 或 canvas 来实现动态图形的绘制,但绘制过程相对较繁琐。对于直观漂亮的进度条,社区也有提供成熟的方案例如 highcharts/ECharts 等等,但基于配置的开发方式终究无法实现 100%的自定义绘制。本文将带你使用 D3.js 从零一步一步实现动态进度条,并分享代码逻辑原理。

基础要求

  • 了解 svg 如何绘制基础图形
  • 了解 D3.js v4 版本
  • 了解如何使用 D3.js (v4)绘制 svg 的基础图形

绘制圆形进度条

对于一个圆形进度条,我们先对其进行任务拆分:

  • 绘制嵌套圆弧
  • 圆心处的实时数据展示
  • 展现动画
  • 美化

1.绘制嵌套圆弧

对于圆形,svg 提供现成的circle标签供使用,但是其劣势在于,对于圆形进度条使用circle可以满足,但对图形进一步扩展时比如绘制半圆,circle的处理就棘手了。D3.js 提供arc相关 API 对圆形的绘制方法进行了封装:

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

arc(); // "M0,-100A100,100,0,0,1,100,0L0,0Z"

上述代码实现了对两个嵌套圆的绘制逻辑,d3.arc()返回一个圆弧构造函数,并通过链式调用设置内圆与外圆的半径大小,起始角度与结束角度。执行arc()构造函数即可获得用于绑定在<path>上的路径数据。完整代码如下:


<!--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>

上述代码实现了 2 个步骤:

  • 1.生成将 0 度作为起点的圆弧构造器arcGenerator
  • 2.设置transform图形偏移量,令图形在画布中央

Js 中文网 – 前端进阶资源教程 https://www.javascriptC.com/,typescript 中文手册
专注分享前端知识,你想要的,在这里都能找到

目前画布上还没有任何元素,接下来我们实际图形的绘制。

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

我们对画布picture添加<path>元素,依据endAngle()特性,使用datum()方法将{endAngle:Math.PI}也就是终点角度绑定到<path>元素上,并将圆弧构造器赋值给path路径d。这样就生成了指定背景颜色的圆弧,实际图形如下:

基于 D3.js 绘制动态进度条

第一个圆弧画好了,那么依据 svg 的层级关系z-index,所谓的进度条其实就是覆盖在第一层圆弧之上的第二层圆弧。同理可得:

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

代码运行后可得:

基于 D3.js 绘制动态进度条

2.圆心处的实时数据展示

第一部分我们已经实现了基于两个path的嵌套圆。第二部分我们来实现圆心处的实时数据展示。 在进度条进行加载时,我们在圆心处添加数据来表达当前的加载进度,使用<text>标签做展示即可:

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

暂时将数据设置为 12,并设置水平居中和垂直居中,效果如下图:

基于 D3.js 绘制动态进度条

3.展现动画

通过 1,2 两部分内容我们已经知道了:

  • 绘制进度条的实质是改变上层弧的角度
  • 当弧度是时为整圆,当弧度是π时为半圆
  • 圆形中的数据即为当前弧度相对的百分比

综上我们只要改变弧度值和数值同时设定改变过程所需时长即可实现所谓”动画”。在 ECharts 提供的官方实例中,通过setInterval来实现每隔固定一段时间进行数据更新,其实在 D3.js 中同样提供了类似方法来实现类似setInterval的功能:

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);
        return arcGenerator(d);
      };
    });
}, 1000);

对这段代码进行拆解:

  • d3.interval()方法提供了setInterval()的功能
  • selection.transition.duration()设置了当前 DOM 属性过渡变化为指定 DOM 属性的过程所需时间,毫秒为单位
  • transation.attrTween为插值功能 API,那么何谓插值?

概括来说,在给定的离散数据中补插函数,可以使这条连续函数通过全部数据点。举个例子,给定一个 div,想实现其背景颜色的从左边红(red)到右边绿(green)的线性渐变,每一区域的色值该如何计算呢?只需:

Js 中文网 – 前端进阶资源教程 https://www.javascriptC.com/,typescript 中文手册
专注分享前端知识,你想要的,在这里都能找到

var compute = d3.interpolate(d3.rgb(255, 0, 0), d3.rgb(0, 255, 0));

compute即为插值函数,参数范围为[0,1],只要你输入该范围内的数字,那么compute函数将返回对应的颜色值。这样的插值有什么用呢?可看下图:

基于 D3.js 绘制动态进度条

假设上图的 div 长度 width 为 100,那么将[0,100]依比例关系转化为[0,10]的范围数据并输入compute函数中,即可得到某一区域对应的颜色。当然,对于线性面积的处理我们不应该使用离散数据作为输入和输出,所以 D3.js 提供更方便的线性渐变 APId3.linear等,这里就不展开描述了。

言归正传,代码d3.interpolate(d.endAngle,Math.random() * Math.PI * 2);实现了如下插值范围:


["当前角度值","随机角度值"] //表达区间而非数组

而后返回一个参数为t的函数,那么该函数的作用是什么呢?
t参数与d类似,是 D3.js 内部实现的插值,其范围为[0,1]。t参数根据设置的duration()时长自动计算在[0,1]内合适的插值数量,并返回插值结果,实现线性平稳的过渡动画效果。

完成滚动条的动画加载效果,我们接下来写圆心实时数据的变化逻辑,只要实现简单的赋值即可,完整代码如下:

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;
        //设置数值
        d3.select('text').text(data.toFixed(0) + '%');
        //将新参数传入,生成新的圆弧构造器
        return arcGenerator(d);
      };
    });
}, 2000);

最终效果如下:

基于 D3.js 绘制动态进度条

4.美化

1,2,3 部分我们实现了最基本的进度条样式和功能,但样式看起来还是很单调的,我们接下来我们对进度条进行线性渐变处理。我们使用 D3.js 提供的线性插值 API:

var colorLinear = d3
  .scaleLinear()
  .domain([0, 100])
  .range(['#EEE685', '#EE3B3B']);

colorLinear同样是一个插值函数,我们输入[0,100]区间中的数值,就会返回对应[”#EEE685″,”#EE3B3B”]区间内的颜色值。比如当进度条显示进度为”80%”时:

var color = colorLinear(80);
//color即为"80%"对应的色值

实现了颜色取值后,我们只需在进度条变化时,将原有颜色改变即可:

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;
        //设置数值
        d3.select('text').text(data.toFixed(0) + '%');
        //将新参数传入,生成新的圆弧构造器
        return arcGenerator(d);
      };
    })
    .styleTween('fill', function(d) {
      return function(t) {
        var data = (d.endAngle / Math.PI / 2) * 100;
        //返回数值对应的色值
        return colorLinear(data);
      };
    });
}, 2000);

styleTweenattrTween类似,是实现改变样式的插值函数。采用链式调用的形式同时对进度条数值和颜色的设置即可。最终实现的效果如下:

基于 D3.js 绘制动态进度条

综上我们实现了在不同数值下颜色变化的圆形进度条,可常用于告警,提醒等业务场景。

绘制矩形进度条

矩形进度条相比圆形进度条简单了很多,同样基于插值原理,平滑改变矩形的长度即可。直接上代码:


<head> <style> #slider { height: 20px; width: 20px; background: #2394F5; margin: 15px; } </style> </head> <body> <div id='slider'></div> <script> d3.interval(function(){ d3.select("#slider").transition() .duration(1000) .attrTween("width", function() { 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>

实现的效果如下:

基于 D3.js 绘制动态进度条

总结

基于 D3.js 绘制进度条的关键点在于插值,从而正确地使图形平滑过渡。如果一定要使用 svg 或纯 css 实现矩形和圆形的进度条当然也是可行的,但对于路径和动画的处理,以及 css 的书写要求都复杂了不少。我们观察到使用 D3.js 绘制上述两种进度条的逻辑代码几乎完全使用 js 实现,同时代码量可以控制在 20 行左右并可封装复用,已经非常精炼了,在自定义图表开发上非常有优势。

对于进度条的衍生版仪表盘图表,相比基础进度条增加了刻度描述和指针计算,但万变不离其宗,只要掌握插值原理和使用,处理类似图表都将得心应手。

作者:ssssyoki
链接:https://juejin.im/post/5a81a8946fb9a06334266f05

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:基于 D3.js 绘制动态进度条

链接:https://www.javascriptc.com/3958.html

« 学习谷歌JavaScript编写风格,这13 点值得我们好好学习!
Js中文网周刊第67期»
Flutter 中文教程资源

相关推荐

QR code