如何用G2构建简单易用的图表库?| 🏆 技术专题第三期征文 ......

2,994 阅读5分钟

前言

17年底 G2 3.0 正式发布开源,我们部门决定将可视化图表库的底层框架统一为 G2,替换掉内部野蛮生长的 Echarts、Highcharts 等。

G2 是一套面向常规统计图表,以数据驱动的高交互可视化图形语法,具有高度的易用性和扩展性。

我们部门面向中后台业务,可视化图表是强需求。开发者涵盖对可视化不熟悉的前端开发,后端开发,合作伙伴开发等等。让大家都从零开始学习 G2 的图形语法,理解“视觉通道”、“数据映射”等专业可视化概念成本太高。因此需要基于 G2 构建一个真正开箱即用的图表库,赋能开发,提升效率,统一视觉效果。

这就是 CloudCharts 的由来,经过3年内部孵化,正式开源,有兴趣的同学可以在官网在线体验:传送门

今天讲讲我们是如何用 G2 构建简单易用的 React 图表库,通过少量配置项即可绘制图表,服务内部近两百个产品。

三板斧

我们针对 G2 在使用中三个最常见的疑问 React化配置化数据映射 做详解,分为这三部分:

1、建立图表生命周期

2、开箱即用包装

3、数据格式映射

建立生命周期

首先搭建组件库整体的架构,目的是让项目能长期的维护和发展,尽量减少可能产生的历史包袱,将 G2 原生的图形语法转化为 React 组件。

我们把组件绘制图表的全部流程列出来:

观察图表绘制的过程,我们可以将其按照组件生命周期划分为几个部分:创建期、存在期、销毁期。这和 React 的生命周期是基本一致的。

我们发现这些绘制流程很大一部分是可以复用的,大多数组件只有“声明绘制逻辑”这一步不一样,以此建立一套生命周期管理机制。

组件代码由原生代码实现,负责声明 G2 的图形语法。中间的工厂函数负责创建图表实例,调用生命周期,以及一些复用的逻辑,最后返回 React 组件。

工厂函数示例

工厂函数是管理生命周期的核心,在这里简单展示工厂函数的代码。实际逻辑会多很多,有兴趣的同学可以去 Github 查看源码:传送门

import G2 from '@antv/g2';
import React from 'react';

function factory(name, component) {
  class CloudCharts extends React.Component {
    componentDidMount() {
      // 设置初始高宽
      this.initSize();
      // 初始化图表
      this.initChart(this.props);
    }

    componentDidUpdate(prevProps) {
      // 配置项有变化,重新生成图表
      this.rerender();
      // 数据有变化,更新数据
      component.changeData(data);
    }

    componentWillUnmount() {
      this.destroy();
    }

    // 初始化适配高宽
    initSize(props) {}

    // 初始化图表
    initChart(props) {
      this.chart = new G2.Chart({
        container: this.chartDom,
      });
      // 调用组件代码
      component.init(this.chart);
    }

    destroy() {
      component.destroy();
    }

    render() {
      return (
        <div ref={dom => (this.chartDom = dom)} />
      );
    }
  }

  CloudCharts.displayName = `CloudCharts${name}`;

  return CloudCharts;
}

开箱即用包装

有了生命周期管理,组件可以专注在图形语法翻译,让用户传入简单的配置项即可得到图表。

我们先来看看 G2 图形语法的有哪些部分:

  • 顶层配置 ✅
  • 数据度量 Scale
  • 坐标系 Crood
  • 坐标轴 Axis ✅
  • 提示信息 Tooltip ✅
  • 图例 Legend ✅
  • 辅助标记 Guide ✅
  • 图形元素 Geom
  • 文本标签 Label ✅

其中打✅的部分逻辑是很容易复用的,可以用函数抽象配置。

组件代码示例

// 折线图 Line
const lineComponent = {
  init(chart, config, data) {
    // 列定义
    const defs = {...}
    // 传入数据
    chart.source(data, defs);
    
    // 设置X轴
    rectXAxis.call(this, chart, config);
    
    // 设置Y轴
    rectYAxis.call(this, chart, config);
    
    // 设置图例
    rectLegend.call(this, chart, config);

    // 设置tooltip
    rectTooltip.call(this, chart, config);

    // 设置辅助线,辅助背景区域
    guide.call(this, chart, config);
    
    // 设置图形元素
    drawLine.call(this, chart, config);
    
    // 其它逻辑
  },
  ...
};

配置项翻译器

我们将组件内接受配置项,声明图形语法的逻辑统称为配置项翻译器。

drawLine 中翻译线图配置项的示例:

if (config.area) {
  chart.area()
    .position(['x', 'y'])
    .color('type', config.colors);
  chart.line()
    .position(['x', 'y'])
    .color('type', config.colors)
    .style('x*y*type*extra', {
      lineJoin: 'round',
    });
} else {
  ...
}

智能化配置项

除了G2自带的配置项外,我们还做了很多智能化配置的包装,降低了使用难度。

  • 自动适配尺寸

  • 自动更新数据

  • 自动更新配置

  • 自动计算时间格式

  • 轴标签个数自动计算

  • 图例过多自动折叠

由于篇幅原因这里就不详细展开,后续还会有文章介绍。

数据格式映射

G2 的数据映射也是很多初学者疑惑的一点,为此我们在工厂函数中做了一层数据转换,允许用户传入 HighCharts 格式的数据。那么内部是怎么实现的呢?

我们来观察一下这张图表,只由一条折线和点组成:

对应的 G2 数据格式是一个非常简单的 JSON 数组,数组中的每一项对应图表的一个数据点:

const data = [
  { year: '1995', value: 4.9 },
  { year: '1996', value: 6 },
  { year: '1997', value: 7 },
  { year: '1998', value: 9 },
  { year: '1999', value: 13 },
];

而Highcharts格式稍微复杂一些,但是关键内容都是一致的:

const data = [
  {
    // name 用于区分多组数据
    name: '折线一',
    data: [
      ['1995', 5],
      ['1996', 6],
      ['1997', 7],
      ['1998', 9],
      ['1999', 13],
    ],
  },
];

所以可以通过循环处理转化为G2的格式:

const newData = [];

data.forEach(oneData => {
  const { name: dataName } = oneData;

  oneData.data.forEach((d, i) => {
    const [x, y, ...extra] = d;
    newData.push({
      x,
      y,
      extra,
      type: dataName,
    });
  });
});

这里返回的 xytype 对应了图形语法中的 field

chart.geom().position(['x', 'y']).color('type')

等于图表库内部固定了 data fields,用户可以使用 HighCharts 格式的数据,这也方便了之前使用 HighCharts 的业务迁移到新图表库中。

写在最后

有了上面的三板斧,整个图表库的框架基本完整,可以愉快的用在业务中了。欢迎大家来使用我们的完整版本:

CloudCharts 欢迎大家 Star、PR、拍砖~

我们在官网还增加了“主题设计器”和“可视化配置”等功能,将会在后续的文章中继续介绍。

团队名片

🏆 技术专题第三期 | 数据可视化的那些事......