作者:禅与计算机程序设计艺术

1.背景介绍

React是一个使用了虚拟DOM的JS库,用来构建用户界面的组件化开发框架。在React中可以采用D3或者其他第三方数据可视化库来实现数据可视化。本文将以D3为例,对React中的D3库进行深入剖析和使用实战。

2.核心概念与联系

D3介绍

D3(Data Driven Documents)是一个JavaScript的图形绘制库,基于Web标准。主要特性包括: 1、简洁的API接口:基于通用语法和直观的数据模型,提供简单而强大的API接口; 2、高度可扩展性:内置丰富的插件机制,通过插件扩展功能; 3、高效的渲染速度:通过矢量合成的方式实现快速、高效的渲染。

React中的D3

在React中可以使用React-d3-library这个第三方库来集成D3到React项目中。该库提供了React Component和D3之间的双向绑定。具体的使用方法如下所示:

1、安装:

npm install --save react-d3-library d3@5

2、导入依赖

import { LineChart } from'react-d3-library';
import * as d3 from 'd3'; // or import 'd3' if using webpack

3、定义数据源

const data = [
  {'time': new Date(2017, 0, 1), 'value': 5}, 
  {'time': new Date(2017, 0, 2), 'value': 6}, 
  {'time': new Date(2017, 0, 3), 'value': 7}];

4、创建LineChart组件并传递props

<LineChart
    xAxisProp="time" 
    yAxisProp="value"
    data={data}>
    <svg width={500} height={300}>
        {/* Add your custom SVG elements here */}
    </svg>
</LineChart>

5、自定义SVG元素

<svg width={500} height={300}>
    <line 
        stroke="#ff7f0e" 
        strokeWidth={2} 
        x1={0} 
        y1={yScale(minY)} 
        x2={xScale(maxX) - xScale(minX)} 
        y2={yScale(minY)} />

    {/* Draw the data points on top of the line */}
    {data.map((d, i) => (
      <circle 
          key={`point${i}`} 
          cx={xScale(d.time)}
          cy={yScale(d.value)} 
          r={3} 
          fill="#000"/>
    ))}

    {/* Customize the X and Y axis labels*/}
    <text 
        textAnchor={"middle"} 
        x={width / 2} 
        y={height - margin.bottom + fontSize}>{yAxisLabel}</text>
    <text 
        transform={`rotate(-90 ${margin.left - fontSize/2} ${height/2})`} 
        textAnchor={"middle"} 
        x={margin.left - fontSize} 
        y={(height - margin.top + margin.bottom)/2}>{xAxisLabel}</text>
</svg>

6、渲染结果

3.核心算法原理和具体操作步骤以及数学模型公式详细讲解

D3的核心算法思想是,将数据映射到SVG元素上,通过JavaScript计算得到坐标信息,然后根据坐标信息填充SVG元素,从而完成数据的可视化。 具体流程如下所示: 1、加载D3.js脚本文件:首先需要在HTML页面的头部引用D3.js文件:

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- Load D3.js library -->
  <script src="//d3js.org/d3.v5.min.js"></script>

  <!-- Other HTML tags -->
</head>
<!-- The rest of the HTML code goes here -->

2、设置画布大小:确定画布的尺寸,以确定坐标轴的范围。这里使用的坐标系是笛卡尔坐标系,即平面直角坐标系,因此需要指定画布的宽度和高度。一般来说,可以使用CSS样式来设置画布的大小:

.chart svg {
  display: block; /* make sure chart is displayed as a block element */
  margin: auto; /* center chart horizontally */
  padding: 20px; /* add some space for margins, borders, etc. */
}

3、获取数据:首先需要准备好数据,并把它传入D3.js的绘图函数中。这里假设有这样一个数组,其中每个对象都代表了一个数据点:

var dataset = [
  {"name": "A", "value": 20},
  {"name": "B", "value": 40},
  {"name": "C", "value": 30},
  {"name": "D", "value": 50},
  {"name": "E", "value": 10}
];

4、选择容器元素:在页面上创建一个容器元素,比如DIV元素,并添加类名为“chart”用于后续定位。这里使用jQuery来选择元素:

var container = $("#container");

5、创建SVG画布:首先调用D3的select()方法选取容器元素,然后调用SVG画布的生成器创建画布,设置画布的尺寸,并返回该画布对象。此处使用了全局变量d3作为D3的命名空间,创建画布的代码如下:

// select the container element and create an SVG canvas within it
var svg = d3.select("#container").append("svg")
 .attr("width", 500)
 .attr("height", 300);

6、绘制圆环:为了更好的表示数据之间的差异,通常会绘制圆环图。创建圆环的方法是在SVG画布上添加一些路径,这些路径将根据坐标系上的位置和半径大小进行填充。圆环的坐标轴设置为百分比比例尺,即最大值设为100%,最小值设为0%。创建圆环的代码如下:

var radius = Math.min(width, height) / 2 - 10; // circle radius
var innerRadius = radius - 30; // ring inside diameter

var pie = d3.pie().sort(null).value(function(d){ return d.value });

// Create the outer arc path
var outerArc = d3.arc()
 .innerRadius(innerRadius)
 .outerRadius(radius);

// Define the paths to draw the arcs between slices
var arcs = svg.selectAll(".arc")
 .data(pie(dataset))
 .enter()
 .append("path")
 .attr("class", "arc")
 .attr("fill", function(d){ return color(d.index); })
 .attr("stroke", "#fff")
 .attr("d", outerArc);

7、绘制文本标签:在SVG画布上添加一些文本标签用于显示数据标签。添加文本的方法可以在SVG画布上创建文本元素,并使用坐标位置和字体样式进行设置。在这里,将数据标签放在圆心旁边,并设置字号为12像素。创建文本的代码如下:

// Add text labels for each slice
arcs.append("text")
 .attr("transform", function(d) { return "translate(" + outerArc.centroid(d) + ")"; })
 .attr("dy", ".35em") // vertical alignment
 .style("font-size", "12px")
 .text(function(d) { return d.data.name + ": " + formatPercent(d.data.value / totalValue); });

function formatPercent(num) {
  return d3.format(".1%")(num);
}

8、调整布局:调整SVG画布的布局,使其居中显示。默认情况下,SVG画布是相对于浏览器窗口进行缩放的,因此可能会出现画布不居中的情况。修改SVG的transform属性即可:

svg.attr("viewBox", "-10 -10 "+(width+20)+" "+(height+20));

完整的函数如下所示:

$(document).ready(function(){

  var dataset = [
    {"name": "A", "value": 20},
    {"name": "B", "value": 40},
    {"name": "C", "value": 30},
    {"name": "D", "value": 50},
    {"name": "E", "value": 10}
  ];

  var container = $("#container");

  // Set up dimensions and scales
  var width = 500;
  var height = 300;
  var radius = Math.min(width, height) / 2 - 10; // circle radius
  var innerRadius = radius - 30; // ring inside diameter

  var color = d3.scaleOrdinal(d3.schemeCategory10); // color scheme
  var totalValue = d3.sum(dataset, function(d){ return d.value; });

  // Define the formats used in labels and tooltips
  var formatNum = d3.format(",.0f");
  var formatPercent = d3.format(".1%");

  // Set up the chart area
  var svg = d3.select("#container").append("svg")
   .attr("width", width)
   .attr("height", height)
   .attr("viewBox", "-10 -10 "+(width+20)+" "+(height+20));

  // Create the pie layout generator with value accessor
  var pie = d3.pie().sort(null).value(function(d){ return d.value });

  // Create the outer arc path
  var outerArc = d3.arc()
   .innerRadius(innerRadius)
   .outerRadius(radius);

  // Define the paths to draw the arcs between slices
  var arcs = svg.selectAll(".arc")
   .data(pie(dataset))
   .enter()
   .append("path")
   .attr("class", "arc")
   .attr("fill", function(d){ return color(d.index); })
   .attr("stroke", "#fff")
   .attr("d", outerArc);

  // Add text labels for each slice
  arcs.append("text")
   .attr("transform", function(d) { return "translate(" + outerArc.centroid(d) + ")"; })
   .attr("dy", ".35em") // vertical alignment
   .style("font-size", "12px")
   .text(function(d) { return d.data.name + ": " + formatPercent(d.data.value / totalValue); });

});

4.具体代码实例和详细解释说明

完整示例代码如下:

import React from'react';
import { LineChart } from'react-d3-library';
import * as d3 from 'd3'; // or import 'd3' if using webpack

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      selectedDate: null,
    };
  }

  componentDidMount() {
    const fetchUrl = '/api/fetchData';

    fetch(fetchUrl)
     .then(response => response.json())
     .then(data => {

        let minTime = Infinity;
        let maxTime = -Infinity;

        data.forEach(({ time }, index) => {
          if (time > maxTime) {
            maxTime = time;
          }

          if (time < minTime) {
            minTime = time;
          }
        });

        console.log('max time:', maxTime);
        console.log('min time:', minTime);

        const dateFormat = d3.timeFormat("%m/%d");

        const formattedData = data.map(({ time, temperature }) => ({
          date: dateFormat(new Date(time)),
          temperature,
        }));

        this.setState({
          data: formattedData,
          minTime,
          maxTime,
        });

      }).catch(error => console.error(error));

  }

  render() {
    const { data, minTime, maxTime } = this.state;

    const xAxisLabel = 'Date';
    const yAxisLabel = 'Temperature';

    return (
      <div className="App">
        <h1>React Temperature Chart Example</h1>
        <LineChart
            xAxisProp="date" 
            yAxisProp="temperature"
            data={data}
            xDomain={{ min: new Date(minTime), max: new Date(maxTime) }}
            marginLeft={60}
            marginTop={40}
            marginRight={60}
            marginBottom={60}
            width={800}
            height={400}
            xAxisLabel={xAxisLabel}
            yAxisLabel={yAxisLabel}>
            <svg></svg>
        </LineChart>
      </div>
    );
  }
}

export default App;

以上是React的数据可视化例子,其中使用的是LineChart组件,传入的数据源包含日期时间字符串和温度值两个属性。页面初始化时先请求数据源,格式化日期属性为JavaScript Date 对象。然后将数据源及相关参数传递给LineChart组件,配置组件的各项参数,如:数据属性名,x轴与y轴属性名,区间范围等。组件接收到参数后,自动绘制折线图,并利用D3进行坐标转换和绘图。

5.未来发展趋势与挑战

目前已有的D3库已经足够满足大多数数据的可视化需求。但是随着社区的不断进步,新的特性和效果也逐渐被开发出来。下一步,D3还将与React结合得更加紧密,通过React-d3-library提供更加友好的API接口,让开发者无需复杂的代码即可实现复杂的数据可视化效果。另外,D3的强大功能也给予了前端开发者极大的灵活性。当然,最重要的是,D3同样也是开源免费的,它是一个开放的平台,任何人都可以参与它的开发。如果希望D3能够更加贴近业务场景,提升研发效率,我们也期待D3社区的共建。

6.附录常见问题与解答

Q:为什么要使用D3? A:React 的诞生就很大程度上受到了 D3 的启发。React 的 JSX 模板语言,使得 React 开发人员可以声明式地描述 UI 组件,而 D3 提供的强大数据处理能力则可以帮助我们更有效地呈现复杂的数据。这种数据驱动视图的架构模式促进了 Web 应用的创新与迭代。

Q:D3 和 React 有什么不同之处? A:D3 是一套基于 JavaScript 的图表绘制工具,适合于桌面端和移动端的应用;而 React 是一款用于构建用户界面的框架,可以用于创建具有交互性的单页应用程序。D3 的 API 更加底层,需要手动管理 DOM 节点;而 React 使用 JSX 语法来声明式地描述组件,减少了开发难度。D3 本身没有绑定 DOM 操作,这使得它可以应用于各种各样的场景;而 React 通过 JSX 在渲染过程中直接更新 UI,因此 React 对性能要求较高。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐