缩略轴(Slider)

概述

G2 中缩略轴(Slider) 可以用于过滤数据,让用户在数据量较大的情况下一次只用关注局部的数据,是一种辅助看数据的组件。它将大量数据浓缩到一个轴上,既可以缩小宏观看数据全貌,又可以放大微观看数据的片段,同时还可以拖拽观察数据在一定区间内的演变。缩略轴可以和 x 或者 y 通道绑定,用于显示不同方向的缩略轴,缩略轴默认都是关闭的。

import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
});

const formatter = (dateTimeString) => {
  return new Date(dateTimeString).toLocaleString();
};

chart.options({
  type: 'line',
  autoFit: true,
  data: {
    type: 'fetch',
    value:
      'https://gw.alipayobjects.com/os/bmw-prod/551d80c6-a6be-4f3c-a82a-abd739e12977.csv',
  },
  encode: { x: 'date', y: 'close' },
  slider: {
    x: {
      labelFormatter: (d) => `${formatter(d)}`,
    },
  },
});

chart.render();

配置层级

缩略轴支持在不同层级进行配置,不同层级的配置具有不同的作用域和特性。

Mark 层级配置

缩略轴可以在 Mark 层级配置。在 G2 中,每个标记(Mark)都有自己的缩略轴。如果标记对应的比例尺是同步的,那么缩略轴也会合并。

({
  type: 'interval',
  slider: {
    x: {},
    y: {},
  },
});

特点:

  • 仅影响当前 Mark
  • 适用于需要对特定标记进行独立过滤的场景
  • 当多个 Mark 共享同一比例尺时,缩略轴会自动合并

View 层级配置

缩略轴也可以在 View 层级配置。缩略轴具有传递性。视图上声明的缩略轴会传递给 children 声明的标记,如果该标记有对应通道的缩略轴,就合并;否则不影响。

({
  type: 'view',
  slider: {
    x: {},
    y: {},
  },
});

特点:

  • 影响整个 View 及其子元素
  • 适用于需要统一控制多个 Mark 的场景
  • 具有传递性,会影响所有子 Mark
  • 优先级高于 Mark 层级配置

何时使用

缩略轴是值域数据的浓缩,它跟位置通道 x, y 对应的比例尺的类型息息相关。一般来说时间类型上使用缩略轴的频率高,连续轴使用缩略轴频次低、分类轴几乎不会使用到缩略轴。

slider
  • 时间轴,数据跨度非常大,【高频使用】,比如一组时序数据跨度 10 年,需观察这期间数据变化趋势时,建议开启缩略轴;
  • 时间轴,数据密度很高,【高频使用】,比如分钟级更新的实时数据,当需要查看一天内的数据走势时,建议开启缩略轴;
  • 连续轴,数据密度很高,【低频使用】比如查看 1-100 岁平均身高分布,开启缩略轴可以全局查看身高分布;
  • 分类轴【不建议使用】。

使用方式

slider
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
});

chart.options({
  type: 'line',
  data: {
    type: 'fetch',
    value:
      'https://gw.alipayobjects.com/os/bmw-prod/551d80c6-a6be-4f3c-a82a-abd739e12977.csv',
  },
  encode: { x: 'date', y: 'close' },
  slider: { y: {} }, // 开启 Y 方向缩略轴
});

chart.render();

配置项

属性 描述 类型 默认值 必选
padding 缩略轴内边距 number | number[] -
values 缩略轴初始选区范围,位于 0 ~ 1 区间 [number, number] -
slidable 是否允许拖动选取和手柄 boolean true
brushable 是否启用刷选 boolean true
labelFormatter 缩略轴拖动手柄标签格式化 (value: number)=>string -
showHandle 是否显示拖动手柄 boolean true
showLabel 是否显示拖动手柄文本 boolean true
showLabelOnInteraction 在调整手柄或刷选时才显示手柄文本,在 showLabel 为 false 时生效 boolean false
autoFitLabel 是否自动调整拖动手柄文本位置 boolean true
style 配置缩略轴组件的样式 style 详见 style

style

配置缩略轴组件的样式。

属性 描述 类型 默认值 必选
selection 配置缩略轴选区的样式 selection 详见 selection
track 配置缩略轴滑轨的样式 track 详见 track
handle 配置缩略轴手柄的样式 handle 详见 handle
sparkline 配置缩略轴迷你图的样式 sparkline 详见 sparkline

selection

配置缩略轴选区的样式 。

属性 描述 类型 默认值 必选
selectionFill 缩略轴选区的填充色 string #1783FF
selectionFillOpacity 缩略轴选区的填充透明度 number 0.15
selectionStroke 缩略轴选区的描边 string -
selectionStrokeOpacity 缩略轴选区的描边透明度 number -
selectionLineWidth 缩略轴选区的描边宽度 number -
selectionLineDash 缩略轴选区描边的虚线配置 [number,number] -
selectionOpacity 缩略轴选区的整体透明度 number -
selectionShadowColor 缩略轴选区的阴影颜色 string -
selectionShadowBlur 缩略轴选区阴影的高斯模糊系数 number -
selectionShadowOffsetX 设置阴影距缩略轴选区的水平距离 number -
selectionShadowOffsetY 设置阴影距缩略轴选区的垂直距离 number -
selectionCursor 缩略轴选区的鼠标样式 string default

在 Slider 组件中配置选区样式的时候,不是以对象的形式来配置,而是以 selection前缀加属性的方式来配置。

({
  slider: {
    x: {
      selectionFill: '#000',
      selectionFillOpacity: 0.9,
      selectionStroke: '#DAF5EC',
      selectionStrokeOpacity: 0.9,
      selectionLineWidth: 2,
      selectionLineDash: [4, 8],
      selectionOpacity: 1,
      selectionShadowColor: '#d3d3d3',
      selectionShadowBlur: 10,
      selectionShadowOffsetX: 10,
      selectionShadowOffsetY: 10,
      selectionCursor: 'pointer',
    },
  },
});

track

配置缩略轴滑轨的样式 。

属性 描述 类型 默认值 必选
trackLength 缩略轴滑轨的长度 number -
trackSize 缩略轴滑轨的尺寸 number 16
trackFill 缩略轴滑轨的填充色 string #416180
trackFillOpacity 缩略轴滑轨的填充透明度 number 1
trackStroke 缩略轴滑轨的描边 string -
trackStrokeOpacity 缩略轴滑轨的描边透明度 number -
trackLineWidth 缩略轴滑轨的描边宽度 number -
trackLineDash 缩略轴滑轨描边的虚线配置 [number,number] -
trackOpacity 缩略轴滑轨的整体透明度 number -
trackShadowColor 缩略轴滑轨的阴影颜色 string -
trackShadowBlur 缩略轴滑轨阴影的高斯模糊系数 number -
trackShadowOffsetX 设置阴影距缩略轴滑轨的水平距离 number -
trackShadowOffsetY 设置阴影距缩略轴滑轨的垂直距离 number -
trackCursor 缩略轴滑轨的鼠标样式 string default

在 Slider 组件中配置选区样式的时候,不是以对象的形式来配置,而是以 track前缀加属性的方式来配置。

({
  slider: {
    x: {
      trackSize: 20,
      trackFill: '#000',
      trackFillOpacity: 0.9,
      trackStroke: '#DAF5EC',
      trackStrokeOpacity: 0.9,
      trackLineWidth: 2,
      trackLineDash: [4, 8],
      trackOpacity: 1,
      trackShadowColor: '#d3d3d3',
      trackShadowBlur: 10,
      trackShadowOffsetX: 10,
      trackShadowOffsetY: 10,
      trackCursor: 'pointer',
    },
  },
});

handle

配置缩略轴手柄的样式。

属性 描述 类型 默认值 必选
handleLabelFontSize 手柄标签的文字大小 number 12
handleLabelFontFamily 手柄标签的文字字体 string -
handleLabelFontWeight 手柄标签的字体粗细 number normal
handleLabelLineHeight 手柄标签文字的行高 number -
handleLabelTextAlign 设置手柄标签文字的水平对齐方式 center | end | left | right | start start
handleLabelTextBaseline 设置手柄标签文字的垂直基线 top | middle | bottom | alphabetic | hanging bottom
handleLabelFill 手柄标签的文字填充色 string #1D2129
handleLabelFillOpacity 手柄标签的文字填充透明度 number 0.45
handleLabelStroke 手柄标签的文字描边 string -
handleLabelStrokeOpacity 手柄标签的文字描边透明度 number -
handleLabelLineWidth 手柄标签文字的描边宽度 number -
handleLabelLineDash 手柄标签文字描边的虚线配置,第一个值为虚线每个分段的长度,第二个值为分段间隔的距离。lineDash 设为[0,0]的效果为没有描边。 [number , number] -
handleLabelOpacity 手柄标签文字的整体透明度 number -
handleLabelShadowColor 手柄标签的文字阴影颜色 string -
handleLabelShadowBlur 手柄标签文字阴影的高斯模糊系数 number -
handleLabelShadowOffsetX 设置阴影距手柄标签文字的水平距离 number -
handleLabelShadowOffsetY 设置阴影距手柄标签文字的垂直距离 number -
handleLabelCursor 手柄标签的鼠标样式 string default
handleLabelDx 手柄标签文字在水平方向的偏移量 number 0
handleLabelDy 手柄标签文字在垂直方向的偏移量 number 0
handleIconRadius 手柄图标的圆角 number 2
handleIconSize 手柄图标的尺寸 number 10
handleIconShape 手柄图标的形状,支持字符串或自定义函数 string | (type: ‘start’ | ‘end’) => DisplayObject -
handleIconFill 手柄图标的填充色 string #f7f7f7
handleIconFillOpacity 手柄图标的填充透明度 number 1
handleIconStroke 手柄图标的描边 string #1D2129
handleIconStrokeOpacity 手柄图标的描边透明度 number 0.25
handleIconLineWidth 手柄图标的描边宽度 number 1
handleIconLineDash 手柄图标描边的虚线配置,第一个值为虚线每个分段的长度,第二个值为分段间隔的距离。lineDash 设为[0,0]的效果为没有描边。 [number , number] -
handleIconOpacity 手柄图标的整体透明度 number -
handleIconShadowColor 手柄图标的阴影颜色 string -
handleIconShadowBlur 手柄图标阴影的高斯模糊系数 number -
handleIconShadowOffsetX 设置阴影距手柄图标的水平距离 number -
handleIconShadowOffsetY 设置阴影距手柄图标的垂直距离 number -
handleIconCursor 手柄图标的鼠标样式 string default

在 Slider 组件中配置滑动手柄属性的时候,不是以对象的形式来配置,而是以 handle前缀加属性的方式来配置。

({
  slider: {
    x: {
      //配置handleLabel的绘图属性
      handleLabelFontSize: 16,
      handleLabelFontFamily: 'sans-serif',
      handleLabelFontWeight: 500,
      handleLabelLineHeight: 20,
      handleLabelTextAlign: 'center',
      handleLabelTextBaseline: 'middle',
      handleLabelFill: '#000',
      handleLabelFillOpacity: 0.9,
      handleLabelStroke: '#DAF5EC',
      handleLabelStrokeOpacity: 0.9,
      handleLabelLineWidth: 2,
      handleLabelLineDash: [4, 8],
      handleLabelOpacity: 1,
      handleLabelShadowColor: '#d3d3d3',
      handleLabelShadowBlur: 10,
      handleLabelShadowOffsetX: 10,
      handleLabelShadowOffsetY: 10,
      handleLabelCursor: 'pointer',

      handleIconSize: 50,
      // 配置handleIcon的绘图属性
      handleIconFill: '#000',
      handleIconFillOpacity: 0.9,
      handleIconStroke: '#DAF5EC',
      handleIconStrokeOpacity: 0.9,
      handleIconLineWidth: 2,
      handleIconLineDash: [4, 8],
      handleIconOpacity: 1,
      handleIconShadowColor: '#d3d3d3',
      handleIconShadowBlur: 10,
      handleIconShadowOffsetX: 10,
      handleIconShadowOffsetY: 10,
      handleIconCursor: 'pointer',
      
      // 自定义手柄图标形状
      handleIconShape: (type) => {
        // type 为 'start' 或 'end',分别表示左右手柄
        return new Circle({
          style: {
            r: 8,
            fill: type === 'start' ? '#FF6B9D' : '#00D9FF',
            stroke: '#fff',
            lineWidth: 2,
          },
        });
      },
    },
  },
});

sparkline

配置缩略轴迷你图的样式。

属性 描述 类型 默认值 必选
sparklineType 迷你图类型:折线图、直方图 line | column line
sparklineIsStack 是否对数据进行堆叠 boolean false
sparklineRange 指定值范围,未指定时将使用 data 的最小值和最大值 [number, number] -
sparklineColor 指定颜色 string | string[] | (index: number)=>string -
sparklineSmooth 适用于折线图,平滑曲线 boolean false
sparklineLineStroke 适用于折线图,线的颜色 string -
sparklineLineStrokeOpacity 适用于折线图,线的透明度 number -
sparklineLineLineDash 适用于折线图,线的虚线配置,第一个值为虚线每个分段的长度,第二个值为分段间隔的距离。lineDash 设为[0, 0]的效果为没有描边。 [number,number] -
sparklineAreaFill 适用于折线图,填充区域的颜色 string -
sparklineAreaFillOpacity 适用于折线图,填充区域的透明度 number -
sparklineColumnFill 适用于直方图,条形的颜色 string -
sparklineColumnFillOpacity 适用于直方图,条形的透明度 number -
sparklineIsGroup 适用于直方图,是否分组显示 boolean false
sparklineSpacing 适用于直方图,分组直方的间距 number 0

在 Slider 组件中配置迷你图属性的时候,不是以对象的形式来配置,而是以 sparkline前缀加属性的方式来配置。

({
  slider: {
    x: {
      sparklineType: 'line',
      sparklineColor: 'red',
    },
  },
});

事件

属性 描述 类型
valuechange 选区发生变化时触发,通过事件监听 ({detail: { value: any; }}) => void
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
});

chart.options({
  type: 'line',
  data: {
    type: 'fetch',
    value:
      'https://gw.alipayobjects.com/os/bmw-prod/551d80c6-a6be-4f3c-a82a-abd739e12977.csv',
  },
  encode: { x: 'date', y: 'close' },
  // 开启 X 轴缩略轴
  slider: { x: {} },
});

chart.on('afterrender', () => {
  const { canvas } = chart.getContext();
  const { document } = canvas;
  document.querySelector('.slider').addEventListener('valuechange', (evt) => {
    console.info(evt.detail);
  });
});

chart.render();

常用交互

滚轮缩放(sliderWheel)

sliderWheel 交互允许用户通过鼠标滚轮或触控板手势来控制缩略轴的选择范围,实现数据范围的快速缩放操作。

  • 触发方式:在图表区域内使用鼠标滚轮或触控板滚动
  • 交互效果:放大缩小选区范围,保持选区中心位置不变
  • 使用场景:适合需要频繁调整数据查看范围的场景

配置方式

({
  slider: {
    x: {}, // 启用 X 轴缩略轴
  },
  interaction: {
    sliderWheel: true, // 启用滚轮缩放交互
  },
});

也可以传入配置项进行自定义:

({
  slider: {
    x: {},
  },
  interaction: {
    sliderWheel: {
      wheelSensitivity: 0.1, // 滚轮灵敏度
      minRange: 0.05,        // 最小缩放范围
      x: true,               // X 轴响应模式
      y: 'shift',            // Y 轴仅在按住 Shift 键时响应
    },
  },
});

主要配置项

属性 描述 类型 默认值
minRange 最小缩放范围 number 0.01
wheelSensitivity 滚轮缩放灵敏度 number 0.05
x X 轴滚轮交互响应模式 boolean | string true
y Y 轴滚轮交互响应模式 boolean | string true

示例

import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
});

chart.options({
  type: 'line',
  autoFit: true,
  data: {
    type: 'fetch',
    value:
      'https://gw.alipayobjects.com/os/bmw-prod/551d80c6-a6be-4f3c-a82a-abd739e12977.csv',
  },
  encode: { x: 'date', y: 'close' },
  slider: {
    x: {
      labelFormatter: (d) => {
        return new Date(d).toLocaleDateString();
      },
    },
  },
  interaction: {
    sliderWheel: {
      wheelSensitivity: 0.08,
      minRange: 0.02,
    },
  },
});

chart.render();

自适应过滤

缩略轴不仅可以用于数据过滤,还支持自适应过滤功能。当拖拽缩略轴时,根据当前选择的数据范围自动调整其他轴的显示范围,提供更好的数据探索体验。

重要说明

自适应策略需要在 sliderFilter 交互中配置,而不是在 slider 组件中配置。 从 G2 5.4.1 开始,当你配置 slider 组件时,会自动启用自适应过滤功能,如果不需要自适应,需要手动关闭。

配置方式

1. 默认自适应(推荐)

配置 slider 组件时,默认启用自适应过滤:

// 默认情况下会自动启用自适应过滤
chart.options(
  slider:{
    x:{}
  }
);

2. 手动配置 sliderFilter

如需自定义自适应策略,在 sliderFilter 交互中配置:

chart.options(
  slider:{
    x:{}
  },
  interaction:{
    sliderFilter:{
      adaptiveMode: 'filter', // 启用自适应
    }
  }
);

3. 手动关闭自适应

如果不需要自适应功能,需要显式关闭:

chart.options(
  slider:{
    x:{}
  },
  interaction:{
    sliderFilter:{
      adaptiveMode: false  // 手动关闭自适应
    }
  }
);

自适应模式参数

通过 sliderFilter 交互中的 adaptiveMode 配置项可以控制自适应过滤的行为:

{
  slider: {
    x: { values: [0.1, 0.8] }
  },
  interaction: {
    sliderFilter: {
      adaptiveMode: 'filter'  // 启用自适应过滤(默认值)
      // adaptiveMode: false   // 禁用自适应过滤
    }
  }
}

可选值:

  • 'filter':启用自适应过滤,根据选定数据范围动态调整其他轴的域值(默认值
  • falsenull:禁用自适应过滤
  • 更多的自适应策略会在后续版本推出,敬请期待。

具体场景

系统会根据不同的配置场景采用不同的自适应策略。在单轴的情况下,slider 配置在 View 层 或 Mark 层效果一样。但在多轴图中,会有不同的自适应策略:

  • 多轴自适应:当图表中存在独立的坐标轴(通过 scale: { y: { independent: true }} 配置)时,会启用多轴自适应策略。根据 slider 配置的层级,会有不同的自适应策略。

    • View 层级配置:影响整个视图中的所有 Mark,适用于需要统一控制多个图表的场景
    • Mark 层级配置:仅影响特定的 Mark,适用于需要对某个特定标记进行独立过滤的场景
  • 自适应计算逻辑:通过收集所有 marks 的通道数据,调用指定的过滤逻辑进行自适应过滤计算,根据当前筛选范围动态计算 Y 轴的 domain 范围,确保显示的数据范围始终合理。

单轴自适应

当只有 X 轴或只有 Y 轴配置了缩略轴时,会启用单轴自适应过滤。拖拽缩略轴时,会根据当前选择的数据范围自动调整另一个轴的显示范围。

单 Mark 场景

import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  autoFit: true,
});

const data = [];
for (let i = 0; i < 150; i++) {
  data.push({
    x: i,
    y: Math.sin(i / 15) * 60 + 80 + Math.random() * 25,
    category: i % 3 === 0 ? 'A' : i % 3 === 1 ? 'B' : 'C',
  });
}

chart.options({
  type: 'point',
  data,
  encode: { x: 'x', y: 'y', color: 'category' },
  slider: {
    x: {
      values: [0.1, 0.8],
      labelFormatter: (d) => `X: ${Math.round(d)}`,
    },
  },
  style: {
    fillOpacity: 0.8,
  },
});

chart.render();
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', autoFit: true });

chart.options({
  type: 'view',
  autoFit: true,
  data: {
    type: 'fetch',
    value: 'https://assets.antv.antgroup.com/g2/stocks.json',
    transform: [{ type: 'filter', callback: (d) => d.symbol === 'GOOG' }],
  },
  slider: {
    x: {},
  },
  children: [
    {
      type: 'area',
      encode: { x: (d) => new Date(d.date), y: 'price' },
      style: { fill: 'linear-gradient(-90deg, white 0%, darkgreen 100%)' },
    },
    {
      type: 'line',
      encode: { x: (d) => new Date(d.date), y: 'price' },
      style: { stroke: 'darkgreen', lineWidth: 2 },
    },
  ],
});

chart.render();
import { Chart } from '@antv/g2';
import { randomPoisson } from 'd3-random';

const random = randomPoisson(Math.pow(10, 2.6));
const chart = new Chart({
  container: 'container',
  autoFit: true,
});

chart.options({
  type: 'rect',
  data: new Array(5000).fill(0).map(random),
  encode: { x: (d) => d },
  transform: [{ type: 'binX', y: 'count' }],
  style: { stroke: 'white' },
  slider: { x: {} },
  tooltip: {
    title: (d, i, data, column) => ({
      value: `${column.x.value[i]} ~ ${column.x1.value[i]}`,
    }),
  },
});

chart.render();
多 Mark 场景

单轴场景下,slider 配置在 View 层 或 Mark 层效果一样。

import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  autoFit: true,
  inset: 3, // 设置内边距,防止自适应过程中 point被截断
});

const data = [
  { time: 0, sales: 200, profit: 150, revenue: 600 }, // 起始高值
  { time: 1, sales: 195, profit: 145, revenue: 580 },
  { time: 2, sales: 190, profit: 140, revenue: 560 },
  { time: 3, sales: 185, profit: 135, revenue: 540 },
  { time: 4, sales: 180, profit: 130, revenue: 520 },
  { time: 5, sales: 175, profit: 125, revenue: 500 },
  { time: 6, sales: 170, profit: 120, revenue: 480 },
  { time: 7, sales: 165, profit: 115, revenue: 460 },
  { time: 8, sales: 160, profit: 110, revenue: 440 },
  { time: 9, sales: 155, profit: 105, revenue: 420 },
  { time: 10, sales: 150, profit: 100, revenue: 400 },
  { time: 11, sales: 145, profit: 95, revenue: 380 },
  { time: 12, sales: 140, profit: 90, revenue: 360 },
  { time: 13, sales: 135, profit: 85, revenue: 340 },
  { time: 14, sales: 130, profit: 80, revenue: 320 },
  { time: 15, sales: 125, profit: 75, revenue: 300 },
  { time: 16, sales: 120, profit: 70, revenue: 280 },
  { time: 17, sales: 115, profit: 65, revenue: 260 },
  { time: 18, sales: 110, profit: 60, revenue: 240 },
  { time: 19, sales: 105, profit: 55, revenue: 220 },
  { time: 20, sales: 100, profit: 50, revenue: 200 },
  { time: 21, sales: 95, profit: 45, revenue: 180 },
  { time: 22, sales: 90, profit: 40, revenue: 160 },
  { time: 23, sales: 85, profit: 35, revenue: 140 },
  { time: 24, sales: 80, profit: 30, revenue: 120 },
];

chart.options({
  type: 'view',
  data,
  children: [
    {
      type: 'line',
      encode: { x: 'time', y: 'sales' },
      style: { stroke: '#1890ff', lineWidth: 2 },
    },
    {
      type: 'line',
      encode: { x: 'time', y: 'profit' },
      style: { stroke: '#52c41a', lineWidth: 2 },
    },
    {
      type: 'point',
      encode: { x: 'time', y: 'revenue', size: 3 },
      style: { fill: '#ff4d4f' },
    },
  ],
  slider: {
    x: {
      labelFormatter: (d) => `时间: ${Math.round(d)}`,
    },
  },
});

chart.render();

多轴自适应(独立坐标轴)

当图表中存在独立的坐标轴(通过 scale: { independent: true } 配置)时,系统会采用多轴自适应策略。根据 slider 的配置层级,会有不同的自适应策略。

1. View 层级 slider

影响整个视图中的所有 Mark,适用于需要统一控制多个图表的场景

import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  autoFit: true,
});

const data = [];
for (let i = 0; i < 60; i++) {
  data.push({
    date: `Day-${i + 1}`,
    sales: Math.sin(i / 10) * 300 + 800 + Math.random() * 200,
    profit: Math.cos(i / 8) * 50 + 75 + Math.random() * 25,
    revenue: Math.sin(i / 12) * 800 + 1500 + Math.random() * 300,
  });
}

chart.options({
  type: 'view',
  data,
  children: [
    {
      type: 'interval',
      encode: { x: 'date', y: 'sales' },
      scale: { y: { nice: true } },
      style: { fill: '#1890ff', fillOpacity: 0.6 },
    },
    {
      type: 'line',
      encode: { x: 'date', y: 'profit' },
      scale: {
        y: {
          key: 'y1',
          independent: true,
          nice: true,
        },
      },
      style: { stroke: '#ff4d4f', lineWidth: 3 },
    },
    {
      type: 'area',
      encode: { x: 'date', y: 'revenue' },
      scale: {
        y: {
          key: 'y2',
          independent: true,
          nice: true,
        },
      },
      style: { fill: '#52c41a', fillOpacity: 0.4 },
    },
  ],
  // View 层级配置缩略轴,自适应同时影响所有轴
  slider: {
    x: {
      labelFormatter: (d) => d,
    },
  },
});

chart.render();
1. Mark 层级 slider

当缩略轴配置在特定 Mark 上时,系统会识别该 Mark 对应的轴,并对共享相同轴的所有 Mark 进行联动自适应。

import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  autoFit: true,
});

const data = [];
for (let i = 0; i < 60; i++) {
  data.push({
    date: `Day-${i + 1}`,
    sales: Math.sin(i / 10) * 300 + 800 + Math.random() * 200,
    profit: Math.cos(i / 8) * 50 + 75 + Math.random() * 25,
    revenue: Math.sin(i / 12) * 800 + 1500 + Math.random() * 300,
  });
}

chart.options({
  type: 'view',
  data,
  children: [
    {
      type: 'interval',
      encode: { x: 'date', y: 'sales' },
      scale: { y: { nice: true } },
      style: { fill: '#1890ff', fillOpacity: 0.6 },
      // Mark 层级 slider,自适应只作用于当前 mark 对应的轴
      slider: {
        x: {
          labelFormatter: (d) => d,
        },
      },
    },
    {
      type: 'line',
      encode: { x: 'date', y: 'profit' },
      scale: {
        y: {
          key: 'y1',
          independent: true,
          nice: true,
        },
      },
      style: { stroke: '#ff4d4f', lineWidth: 3 },
    },
    {
      type: 'area',
      encode: { x: 'date', y: 'revenue' },
      scale: {
        y: {
          key: 'y2',
          independent: true,
          nice: true,
        },
      },
      style: { fill: '#52c41a', fillOpacity: 0.4 },
    },
  ],
});

chart.render();

自适应特性

离散轴与连续轴

  • 连续轴:自适应时会计算数据的最小值和最大值,形成新的域值范围
  • 离散轴:自适应时会收集所有在范围内的离散值,去重后作为新的域值
  • 零基线保持:对于原始域值包含 0 的连续轴,自适应时会保持零基线

双向联动

如果 X 轴与 Y 轴都配置了缩略轴,即使配置了自适应策略,也不会生效。

import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
});

const data = [];
for (let i = 0; i < 300; i++) {
  const x = Math.random() * 100;
  const y = x * 0.7 + Math.random() * 30 + 10;
  data.push({
    x,
    y,
    size: Math.random() * 8 + 3,
    category: Math.random() > 0.5 ? 'A' : 'B',
  });
}

chart.options({
  type: 'point',
  data,
  encode: {
    x: 'x',
    y: 'y',
    size: 'size',
    color: 'category',
  },
  // 同时配置 X 和 Y 轴缩略轴,此时自适应不会生效
  slider: {
    x: {
      values: [0.2, 0.8],
      labelFormatter: (d) => `X: ${Math.round(d)}`,
    },
    y: {
      values: [0.1, 0.9],
      labelFormatter: (d) => `Y: ${Math.round(d)}`,
    },
  },
  scale: {
    x: { nice: true },
    y: { nice: true },
  },
  style: {
    fillOpacity: 0.7,
  },
});

chart.render();

示例

自定义手柄图标

缩略轴支持自定义手柄图标的形状,可以通过 handleIconShape 属性实现。

import { Chart } from '@antv/g2';
import { Circle } from '@antv/g';

const chart = new Chart({
  container: 'container',
  autoFit: true,
});

chart.options({
  type: 'line',
  data: {
    type: 'fetch',
    value:
      'https://gw.alipayobjects.com/os/bmw-prod/551d80c6-a6be-4f3c-a82a-abd739e12977.csv',
  },
  encode: { x: 'date', y: 'close' },
  slider: {
    x: {
      labelFormatter: (d) => new Date(d).toLocaleDateString(),
      style: {
        // 自定义手柄图标形状
        handleIconShape: (type) => {
          // type 参数为 'start' 或 'end',分别表示左右手柄
          return new Circle({
            style: {
              r: 8,
              fill: type === 'start' ? '#1890FF' : '#52C41A',
              stroke: '#fff',
              lineWidth: 2,
              shadowColor: type === 'start' ? '#1890FF' : '#52C41A',
              shadowBlur: 10,
            },
          });
        },
        handleIconSize: 16,
      },
    },
  },
});

chart.render();

说明:

  • handleIconShape 可以是字符串(如 'circle')或自定义函数
  • 函数接收 type 参数,值为 'start''end',分别对应左右手柄
  • 函数需要返回一个 DisplayObject 对象(如 CirclePath 等)
  • 可以为不同的手柄设置不同的样式,增强视觉区分度

自定义缩略轴(Slider)

如果不希望使用 G2 默认的缩略轴,就可以按照以下几步自定义:

  • 在渲染结束后渲染 slider。
  • 监听 slider 事件。

第一步的的关键是通过 chart.getCoordinate 获得的 coordinate 对象确定 slider 的位置和长度。第二步的关键是通过 chart.getScale 获得 scale 对选择的范围进行 invert,最后获得选择的数据范围,然后更新 scale 的定义域。

import * as G2 from '@antv/g2';

const { Chart } = G2;
const chart = new Chart({
  container: 'container',
});
const container = chart.getContainer();

function sliderX(chart) {
  // 创建并且挂载 range
  const container = chart.getContainer();
  const range = document.createElement('input');
  container.append(range);

  // 根据 coordinate 设置 range 的宽度等属性
  const coordinate = chart.getCoordinate();
  const { paddingLeft, width } = coordinate.getOptions();
  range.type = 'range';
  range.min = 0;
  range.max = width;
  range.value = width;
  range.style.display = 'block';
  range.style.width = width + 'px';
  range.style.marginLeft = paddingLeft + 'px';

  // 监听 change 事件,通过 scale 获得筛选得到的 domain
  // 更新 domain 并且渲染
  const scale = chart.getScaleByChannel('x');
  const options = chart.options();
  range.onchange = (event) => {
    const value = event.target.value;
    const range = [0, value / width];
    const domain = range.map((d) => scale.invert(d));
    chart.options({
      ...options,
      scale: { x: { domain } },
    });
    chart.render();
  };
}

// 渲染图表
chart.options({
  type: 'line',
  data: {
    type: 'fetch',
    value:
      'https://gw.alipayobjects.com/os/bmw-prod/551d80c6-a6be-4f3c-a82a-abd739e12977.csv',
  },
  encode: { x: 'date', y: 'close' },
});

chart.render().then(sliderX);

Mark 层级配置示例

Mark 层级配置仅影响特定的 Mark,适用于需要对某个特定标记进行独立过滤的场景:

import { Chart } from '@antv/g2';

const chart2 = new Chart({
  container: 'container',
  autoFit: true,
});

// 标准的G2数据结构 - 不同类型的数据,添加极高值展示自适应效果
const data2 = [
  { category: 'A', time: 0, value: 45, type: 'primary' },
  { category: 'A', time: 1, value: 52, type: 'primary' },
  { category: 'A', time: 2, value: 48, type: 'primary' },
  { category: 'A', time: 3, value: 10, type: 'primary' },
  { category: 'A', time: 4, value: 55, type: 'primary' },
  { category: 'A', time: 5, value: 67, type: 'primary' },
  { category: 'A', time: 6, value: 43, type: 'primary' },
  { category: 'A', time: 7, value: 66, type: 'primary' },
  { category: 'A', time: 8, value: 63, type: 'primary' },
  { category: 'A', time: 9, value: 71, type: 'primary' },
  { category: 'A', time: 10, value: 58, type: 'primary' },
  { category: 'A', time: 11, value: 44, type: 'primary' },
  { category: 'A', time: 12, value: 49, type: 'primary' },
  { category: 'A', time: 13, value: 66, type: 'primary' },
  { category: 'A', time: 14, value: 54, type: 'primary' },
  { category: 'B', time: 0, value: 32, type: 'secondary' },
  { category: 'B', time: 1, value: 38, type: 'secondary' },
  { category: 'B', time: 2, value: 29, type: 'secondary' },
  { category: 'B', time: 3, value: 44, type: 'secondary' },
  { category: 'B', time: 4, value: 41, type: 'secondary' },
  { category: 'B', time: 5, value: 20, type: 'secondary' },
  { category: 'B', time: 6, value: 28, type: 'secondary' },
  { category: 'B', time: 7, value: 39, type: 'secondary' },
  { category: 'B', time: 8, value: 35, type: 'secondary' },
  { category: 'B', time: 9, value: 48, type: 'secondary' },
  { category: 'B', time: 10, value: 35, type: 'secondary' },
  { category: 'B', time: 11, value: 42, type: 'secondary' },
  { category: 'B', time: 12, value: 31, type: 'secondary' },
  { category: 'B', time: 13, value: 45, type: 'secondary' },
  { category: 'B', time: 14, value: 37, type: 'secondary' },
  { category: 'C', time: 0, value: 28, type: 'tertiary' },
  { category: 'C', time: 1, value: 31, type: 'tertiary' },
  { category: 'C', time: 2, value: 25, type: 'tertiary' },
  { category: 'C', time: 3, value: 35, type: 'tertiary' },
  { category: 'C', time: 4, value: 185, type: 'tertiary' }, // 极高值
  { category: 'C', time: 5, value: 38, type: 'tertiary' },
  { category: 'C', time: 6, value: 22, type: 'tertiary' },
  { category: 'C', time: 7, value: 29, type: 'tertiary' },
  { category: 'C', time: 8, value: 45, type: 'tertiary' },
  { category: 'C', time: 9, value: 36, type: 'tertiary' },
  { category: 'C', time: 10, value: 31, type: 'tertiary' },
  { category: 'C', time: 11, value: 27, type: 'tertiary' },
  { category: 'C', time: 12, value: 105, type: 'tertiary' }, // 极高值
  { category: 'C', time: 13, value: 33, type: 'tertiary' },
  { category: 'C', time: 14, value: 29, type: 'tertiary' },
];

chart2.options({
  type: 'view',
  data: data2,
  children: [
    {
      type: 'interval',
      data: {
        value: data2,
        transform: [{ type: 'filter', callback: (d) => d.type === 'primary' }],
      },
      encode: { x: 'time', y: 'value' },
      style: { fill: '#1890ff', fillOpacity: 0.6 },
      // Mark 层级配置 - 仅控制柱状图的过滤
      slider: {
        x: {
          values: [0.2, 0.7],
          labelFormatter: (d) => `柱状图: ${Math.round(d)}`,
        },
      },
    },
    {
      type: 'line',
      data: {
        value: data2,
        transform: [
          { type: 'filter', callback: (d) => d.type === 'secondary' },
        ],
      },
      encode: { x: 'time', y: 'value' },
      style: { stroke: '#52c41a', lineWidth: 2 },
    },
    {
      type: 'point',
      data: {
        value: data2,
        transform: [{ type: 'filter', callback: (d) => d.type === 'tertiary' }],
      },
      encode: { x: 'time', y: 'value', size: 4 },
      style: { fill: '#ff4d4f', fillOpacity: 0.8 },
    },
  ],
  scale: {
    x: { nice: true },
    y: { nice: true },
  },
});

chart2.render();
全站搜索
文档与示例输入关键词开始搜索

输入关键词,查找文档、API 与示例

选择打开Esc关闭