响应式图表

阅读时间约 4 分钟

概述

响应式图表 (responsive plot) 的目的是为了保证图表在任何数据和显示尺寸下的基本可读性问题。

在现实的图表应用场景中,一个棘手的难题是图表的展示空间往往并不足够显示图表的数据量。我们的业务数据可能呈现多种形态,不一定是理想数据;同时图表的使用场景也经常是在一个上下文语境中,比如在文本中插入的图表、中台系统、dashboard 等,这决定了图表未必能获得理想的展示空间。

图表数据量和展示空间的矛盾会造成图表布局错乱、组件遮挡图形、组件各部分相互重叠等问题。下图是两个典型案例:

这种非常典型的信息密度 (information density) 过载现象正是 G2Plot 响应式模块所要解决的问题 — 当信息密度过高时,对图表辅助信息进行抽稀,保证图表主要信息的展示,以使图表的信息密度达到合理平衡。

g2plot 响应式方案由三块组成:constraints(约束条件)、rules(响应规则) 和 responsiveTheme(响应式主题)。基本思路是:首先定义图表合理信息密度的衡量标准 (constraints),然后去判断图表各组成部分是否满足该标准,如果不满足标准,则对图表应用响应规则 (rules)。这个过程是 iterative 的,如果不满足约束条件,响应规则会被依次执行下去。

一个约束条件 (constraint) 对应一组响应规则 (rules),多个约束组构成一个完整的响应式方案 — resonsiveTheme。

Constraint (约束条件)

可以将 constraint(约束条件) 理解为一个判断方法,定义某图表组成部分的信息密度衡量标准,并判断该部分是否符合该标准。

下面的例子中声明了一个 axis(坐标轴)的约束条件,该条件判断坐标轴 label 之间的间距是否大于 4 像素。

axis: {
  constraint:{
    name: 'elementDist',
    option:{
      value: 4,
    },
  },
},

约束库

g2plot 内置了一套常用的 constraint library,目前响应式模块还在试验和打磨阶段,该库将会持续更新:

constraint nameoptiontypeusageintro
elementDistvalue: Numberchaincompare两元素之间的距离
elementDistVerticalvalue: Numberchaincompare两元素之间的纵向距离
elementDistHorizontalvalue: Numberchaincompare两元素之间的横向距离
elementWidthratio: Numberpaddingcompare元素的宽度占母级元素的比例
elementCollisiongroupcompare两元素是否发生碰撞
columnWidthratio: Numberpaddingassign柱形宽度
ringThicknessratio: Numberpaddingassign环形宽度
minRingThicknessvalue: Numberpaddingassign最小环形宽度

注 1:type 的含义

  • chain 类型的 constraint 定义的是一列有序 node,前后两 node 之间的约束关系
  • padding 类型的 constraint 定义的是有子母级关系的两个 nodes 之间的约束关系
  • group 类型的 constraint 定义的一列 nodes,node 与 node 之间的两两约束关系,label 与 label 之间的碰撞检测就属于 group 类型

注 2:usage 的含义

  • compare:判断方法,出参为 boolean
  • assign:赋值方法

注册自定义约束条件

g2plot 允许用户注册自己的约束条件,允许覆盖现有约束库中的条件。

function myConstraint(parameters, cfg?) {}

plot.registerConstraint(name, {
  type: 'padding',
  usage: 'compare',
  method: myConstraint,
});

Rules (响应规则)

响应规则是当 constraint(约束条件)不满足的时候,对图表组件的优化策略。一个 constraint 可能对应着多个优化策略,这些优化策略将按配置的顺序执行。

规则库

g2plot 内置了一套常用的 rule library,目前响应式模块还在试验和打磨阶段,该库将会持续更新:

rule nameoptionintro
textWrapperlineNumber: Number文本折行,可设置每行字数或折几行
textRotationdegree: Number文本旋转,可设置旋转角度
textAbbrevaiteabbreviateBy: string文本省略,可设置省略头部、尾部或中间
textHide文本消失
digitAbbraviateunit: 'k'  'm' 
dateTimeStringAbbraviatekeep: any[]时间文本周期显示,可设置保头保尾或保留任意一 index
textAbbraviateabbreviateBy?: 'start'  'middle' 
robustAbbraviateunit: 'k'  'm' 
nodesSamplingkeep: array抽样显示,可设置保头保尾或任意一个 index
nodesSamplingByAbbraviate配合 dateTimeAbbraviate 方法使用,抽样掉经过省略的文本,保留完整的文本。
nodeSamplingByChange根据数据间的变化进行抽样,保留变化较大的点
nodeSamplingByStatestate: {name: string,
    exp: string }
function
nodeJitter元素在水平和垂直方向抖开
nodeJitterUpward元素向上抖开并拉线
clearOverlapping在一组元素中去除 overlap 的元素,当多个元素发生重叠时,只保留位于最高点的元素。此方法用于多折线数据点 label 的 overlapping。

注册自定义响应规则

g2plot 允许用户注册自己的响应规则,允许覆盖现有规则库中的规则。

function myRule() {}
plot.registerResponsiveRule(name, myRule);

ResponsiveTheme

多个 constraints 和 rules 的组合构成响应式图表的完整解决方案 —  responsiveTheme(响应式主题)。G2Plot 内部提供一套默认好用的响应式主题,同时也向用户开放注册自己的响应式主题。

responsiveTheme 的结构

一个完整的 responsiveTheme 的结构按图表构成进行组织,定义了图表内各组件的约束条件和该条件的响应规则。

以柱状图为例,柱状图参与响应式的图表元素为图形 (column)、坐标轴 (axis)、图形标签 (label),它的 responsiveTheme 结构如下:

const responsiveTheme = {
  axis: {
    x: {
      category: {
        constraints: [],
        rules: [],
      },
    },

    y: {
      linear: {
        constraints: [],
        rules: [],
      },
    },
  },

  label: {
    constraints: [],
    rules: [],
  },

  column: {
    constraints: [],
  },
};

注册 responsiveTheme

用户在注册自己的 responsiveTheme 时,可以自由的调用和组装 contraints 库和 rules 库中的方法。

调用一个 constraint 方法:

// 调用elementDist约束条件,并声明元素最小间距为8
{
  name: 'elementDist',
  option: {
    value: 8,
  },
}

调用一个 rule 方法:

// 调用textAbbravaite响应规则,并声明省略文字的尾部
{
  name: 'textAbbrevaite',
  option:{
    abbraviateBy: 'end',
  },
}

组装:

const lineTheme = {
  label: {
    constraints: [{
      name: 'elementDist',
      option:{
        value: 8,
      },
    }],
    rules: {
      elementCollision: [{ name: 'nodesResamplingByChange' }, { name: 'clearOverlapping' }],
    },
  },
});
registerResponsiveTheme('line', lineTheme);