几何元素
Interval
条形图
// https://g2.antv.vision/zh/examples/column/basic#basic
const data = [
{ year: '1951 年', sales: 38 },
{ year: '1952 年', sales: 52 },
{ year: '1956 年', sales: 61 },
{ year: '1957 年', sales: 145 },
{ year: '1958 年', sales: 48 },
{ year: '1959 年', sales: 38 },
{ year: '1960 年', sales: 38 },
{ year: '1962 年', sales: 38 },
];
const options = {
type: 'interval',
data,
encode: {
x: 'year',
y: 'scales'
}
};
区间条形图
// https://g2.antv.vision/zh/examples/column/basic#ranged
const data = [
{ x: '分类一', y1: 76, y2: 100 },
{ x: '分类二', y1: 56, y2: 108 },
{ x: '分类三', y1: 38, y2: 129 },
{ x: '分类四', y1: 58, y2: 155 },
{ x: '分类五', y1: 45, y2: 120 },
{ x: '分类六', y1: 23, y2: 99 },
{ x: '分类七', y1: 18, y2: 56 },
{ x: '分类八', y1: 18, y2: 34 },
];
const options = {
type: 'interval',
data,
encode: {
x: 'x',
y: ['y1', 'y2']
}
}
堆积条形图
// https://g2.antv.vision/zh/examples/column/stack#stacked
const data = [
{ name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 },
{ name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 },
{ name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 },
{ name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 },
{ name: 'London', 月份: 'May', 月均降雨量: 47 },
{ name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 },
{ name: 'London', 月份: 'Jul.', 月均降雨量: 24 },
{ name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 },
{ name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 },
{ name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 },
{ name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 },
{ name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 },
{ name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 },
{ name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 },
{ name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 },
{ name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 },
];
const options = {
type: 'interval',
data,
statistic: [{type: 'stackY'}],
encode: {
x: '月份',
y: '月均降雨量',
color: 'name',
}
}
- [待定:] 如果没有设置 stackY,是否默认推断。如果默认推断,如何关闭
百分比堆叠柱状图
```javascript // https://g2.antv.vision/zh/examples/column/stack#stacked-percentage
const data = [ { country: ‘Europe’, year: ‘1750’, value: 163 }, { country: ‘Europe’, year: ‘1800’, value: 203 }, { country: ‘Europe’, year: ‘1850’, value: 276 }, { country: ‘Europe’, year: ‘1900’, value: 408 }, { country: ‘Europe’, year: ‘1950’, value: 547 }, { country: ‘Europe’, year: ‘1999’, value: 729 }, { country: ‘Europe’, year: ‘2050’, value: 628 }, { country: ‘Europe’, year: ‘2100’, value: 828 }, { country: ‘Asia’, year: ‘1750’, value: 502 }, { country: ‘Asia’, year: ‘1800’, value: 635 }, { country: ‘Asia’, year: ‘1850’, value: 809 }, { country: ‘Asia’, year: ‘1900’, value: 947 }, { country: ‘Asia’, year: ‘1950’, value: 1402 }, { country: ‘Asia’, year: ‘1999’, value: 3634 }, { country: ‘Asia’, year: ‘2050’, value: 5268 }, { country: ‘Asia’, year: ‘2100’, value: 7268 }, ];
const options = { type: ‘interval’, data, statistic: [ {type: ‘stackY’}, {type: ‘normalizeY’} ], encode: { x: ‘year’, y: ‘value’, color: ‘country’, } }
<a name="nqFVT"></a>
### 分组条形图
![image.png](https://cdn.nlark.com/yuque/0/2022/png/418707/1646391909897-3457544e-0ecf-4d44-98e7-811579afbb25.png#clientId=uf3befbf1-0c05-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=358&id=u483cbcb7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=716&originWidth=1278&originalType=binary&ratio=1&rotation=0&showTitle=false&size=195960&status=done&style=none&taskId=u7bfb448f-cec7-4d64-b2c2-3d16b01a500&title=&width=639)
```javascript
// https://g2.antv.vision/zh/examples/column/stack#stacked
const data = [
{ name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 },
{ name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 },
{ name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 },
{ name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 },
{ name: 'London', 月份: 'May', 月均降雨量: 47 },
{ name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 },
{ name: 'London', 月份: 'Jul.', 月均降雨量: 24 },
{ name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 },
{ name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 },
{ name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 },
{ name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 },
{ name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 },
{ name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 },
{ name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 },
{ name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 },
{ name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 },
];
const options = {
type: 'interval',
data,
statistic: [{type: 'dodgeX'}], // dodgeX 推断出 series
encode: {
x: '月份',
y: '月均降雨量',
color: 'name',
}
}
// 下面的写法也是可以的
const options = {
type: 'interval',
data,
encode: {
x: '月份',
y: '月均降雨量',
series: 'name',
color: 'name',
}
}
饼图
// https://g2.antv.vision/zh/examples/pie/basic#labelline
const data = [
{ item: '事例一', count: 40},
{ item: '事例二', count: 21},
{ item: '事例三', count: 17},
{ item: '事例四', count: 13},
{ item: '事例五', count: 9},
];
const options = {
type: 'interval',
statistic: [{type: 'percentageY'}, {type: 'stackY'}],
coordinate: [{type: 'polar'}],
ecnode: {
y: 'count',
color: 'item'
}
}
漏斗图
// https://g2.antv.vision/en/examples/funnel/funnel#pyramid
const data = [
{ action: '浏览网站', pv: 50000 },
{ action: '放入购物车', pv: 35000 },
{ action: '生成订单', pv: 25000 },
{ action: '支付订单', pv: 15000 },
{ action: '完成交易', pv: 8000 },
];
const options = {
type: 'interval',
statistic: [{ type: 'symmetryY' }],
coordinate: [ { type: 'transpose'}, { type: 'reflectY' }],
ecnode: {
y: 'count',
x: 'action',
color: 'action',
shape: 'funnel'
}
}
Area
河流图
// https://g2.antv.vision/zh/examples/area/streamgraph#streamgraph
const options = {
type: 'area',
statistic: [{ type: 'stackY' }, { type: 'symmetryY' }],
encode: {
x: 'year',
y: 'count',
}
}
雷达图
// https://g2.antv.vision/zh/examples/radar/radar#basic
const users = [
{ item: 'Design', user: 'a', score: 70 },
{ item: 'Design', user: 'b', score: 30 },
{ item: 'Development', user: 'a', score: 60 },
{ item: 'Development', user: 'b', score: 70 },
{ item: 'Marketing', user: 'a', score: 50 },
{ item: 'Marketing', user: 'b', score: 60 },
{ item: 'Users', user: 'a', score: 40 },
{ item: 'Users', user: 'b', score: 50 },
{ item: 'Test', user: 'a', score: 60 },
{ item: 'Test', user: 'b', score: 70 },
{ item: 'Language', user: 'a', score: 70 },
{ item: 'Language', user: 'b', score: 50 },
{ item: 'Technology', user: 'a', score: 50 },
{ item: 'Technology', user: 'b', score: 40 },
{ item: 'Support', user: 'a', score: 30 },
{ item: 'Support', user: 'b', score: 40 },
{ item: 'Sales', user: 'a', score: 60 },
{ item: 'Sales', user: 'b', score: 40 },
{ item: 'UX', user: 'a', score: 50 },
{ item: 'UX', user: 'b', score: 60 },
];
const options = {
type: 'layer',
data,
coordinates: [{ type: 'polar' }],
encode: {
x: 'item',
y: 'score',
color: 'user'
},
style: {
fillOpacity: 0.4,
strokeOpacity: 0.4,
},
children: [
{type: 'area'},
{type: 'point'},
],
}
Line
平行坐标系
// https://vega.github.io/vega/examples/parallel-coordinates/
const response = await fetch('https://vega.github.io/vega/data/cars.json');
const data = await response.json();
const options = {
type: 'line',
data,
coordainte: [{type: 'parallel'}],
component: [
{type: 'axisY', channel: 'position[0]', title: 'HHH'}
],
scale: {
'position[0]': { nice: true },
},
encode: {
position: [
'Cylinders',
'Displacement',
'Weight_in_Lbs',
'Horsepower',
'Acceleration',
'Miles_per_Callon',
'Year',
],
}
}
Text
词云图
// https://vega.github.io/vega/examples/word-cloud/
const options = {
type: 'text',
data: '...',
transform: [
{type: 'split'},
{type: 'wordcloud'} // 这是个异步的操作
],
scale: {
color: {
range: ["#d5a928", "#652c90", "#939597"]
}
},
encode: {
text: 'text',
x: 'x',
y: 'y',
fontSize: 'fontSize',
rotation: 'rotation',
color: 'text',
},
};
Annotation
// https://g2.antv.vision/zh/examples/case/line#line7
const options = {
type: 'layer',
data,
encode: {
x: 'Date',
y: 'Close'
},
children: [
{
type: 'line'
},
{
type: 'text',
statistic: [
{type: 'selectMinY'}
],
encode: {
text: d => `全部谷值:${d.Close}`
}
},
{
type: 'text',
statistic: [
{type: 'selectMaxY'}
],
encode: {
text: d => `全部峰值:${d.Close}`
}
}
],
};
外部 Label
// https://g2.antv.vision/zh/examples/case/column#column2
const data = [
{ type: '未知', value: 654, percent: 0.02 },
{ type: '17 岁以下', value: 654, percent: 0.02 },
{ type: '18-24 岁', value: 4400, percent: 0.2 },
{ type: '25-29 岁', value: 5300, percent: 0.24 },
{ type: '30-39 岁', value: 6200, percent: 0.28 },
{ type: '40-49 岁', value: 3300, percent: 0.14 },
{ type: '50 岁以上', value: 1500, percent: 0.06 },
];
// 这是其中一种写法,text,interval 是同一层级的东西
const config = {
type: 'layer',
encode: {
x: 'type',
y: 'value'
},
children: [
{ type: 'interval'},
{
type: 'text',
encode: { text: 'value' },
style: { dy: '-2em' }
},
{
type: 'text',
encode: { text: 'percent' },
style: { dy: '-1em' }
}
],
}
// 这是第二种写法,text 是 interval 的一部分
// 这样 text 就可以感知 interval 的 bounding box
const options = {
type: 'interval',
data,
encode: {
x: 'type',
y: 'value'
},
children: [
{
type: 'text', // 默认位置是父亲元素 bounding box 的左上角
encode: { text: 'value' },
style: { dy: '-2em' }
},
{
type: 'text',
encode: { text: 'percent' },
style: { dy: '-1em' }
}
]
};
内部 Label
// https://g2.antv.vision/zh/examples/relation/relation#treemap
const options = {
type: 'polygan',
data,
transform: [
{type: 'treemap'}
],
encode: {
x: 'x',
y: 'y'
},
children: [
{
type: 'text',
encode: {
x: 0.5, // 指定为中心
y: 0.5, // 指定为中心
text: 'label',
},
// scale: {
// x: {type: 'identity'},
// y: {type: 'identity'}
// }
}
]
}
Image
散点图
// https://g2.antv.vision/zh/examples/point/bubble#bubble-image
const data = [
{ name: 'Internet Explorer', value: 26, imageURL: ''},
{ name: 'Chrome', value: 40, imageURL: ''},
{ name: 'Firefox', value: 30, imageURL: ''},
{ name: 'Safari', value: 24, imageURL: ''},
{ name: 'Opera', value: 15, imageURL: ''},
{ name: 'Undetectable', value: 8, imageURL: ''}
];
const options = {
type: 'layer',
children: [
{
type: 'edge',
x: ['name', 'name'],
y: ['value', 1],
shape: 'dash'
},
{
type: 'image',
data,
scale: {
size: {type: 'pow'}
},
encode: {
x: 'name',
y: 'value',
url: 'imageURL',
size: 'value'
}
}
]
};
Mix
桑基图
// https://g2.antv.vision/zh/examples/case/sankey/#sankey-card
const options = {
type: 'layer',
data,
transform: [
{ type: 'sankey', /** 更多参数 **/ } // 在这里计算
],
children: [
{
transform: [{type: 'pick', fields: ['nodes']}],
type: 'polygon',
encode: {
x: 'x',
y: 'y,
}
},
{
transform: [{type: 'pick', fields: ['edges']}],
type: 'edge',
encode: {
shape: 'arc',
x: 'x',
y: 'y'
}
}
]
};
双轴柱线图
// https://g2.antv.vision/zh/examples/other/other#double-axes
const data = [
{ time: '10:10', call: 4, waiting: 2, people: 2 },
{ time: '10:15', call: 2, waiting: 6, people: 3 },
{ time: '10:20', call: 13, waiting: 2, people: 5 },
{ time: '10:25', call: 9, waiting: 9, people: 1 },
{ time: '10:30', call: 5, waiting: 2, people: 3 },
{ time: '10:35', call: 8, waiting: 2, people: 1 },
{ time: '10:40', call: 13, waiting: 1, people: 2 }
];
const options = {
type: 'layer',
data,
encode: {x: 'time'},
sync: {
y: false
},
children: [
{
type: 'interval',
encode: {
y: 'waiting',
color: '#3182bd',
}
},
{
type: 'layer',
encode: {
y: 'people',
color: '#fdae6b'
},
component: [
// 没有这条声明轴会都在右边
// 因为这两者的 scale 没有同步
{type: 'axisY', position: 'left'}
],
children: [
{type: 'point'},
{type: 'line'}
]
}
],
};
这种情况可能需要一种同步两边刻度的算法,但是只能保证一边刻度的可读性。
柱状双轴图
// https://codesandbox.io/s/dual-axes-of-column-5xk0pg?file=/index.ts
const data = [
{ year: "1991", value: 3, count: 10 },
{ year: "1992", value: 4, count: 4 },
{ year: "1993", value: 3.5, count: 5 },
{ year: "1994", value: 5, count: 5 },
{ year: "1995", value: 4.9, count: 4.9 },
{ year: "1996", value: 6, count: 35 },
{ year: "1997", value: 7, count: 7 },
{ year: "1998", value: 9, count: 1 },
{ year: "1999", value: 13, count: 20 }
];
const options = {
type: 'layer',
data,
sync: {
y: false,
},
encode: {
x: 'year',
y: 'value',
},
scale: {
series: {
range: ['a', 'b'] // 指定一共有两个序列
}
},
children: [
{
type: 'interval',
encode: {
series: 'a', // 指定是序列 a
}
},
{
type: 'interval',
encode: {
series: 'b', // 指定是序列 b
}
}
]
};
比例尺
分段映射
暂时用双轴图代替
// https://github.com/antvis/G2/issues/2712
const options = {
type: 'line',
data,
scale: {
y: {
domain: [0, 80, 100, 105],
range: [0, 0.5, 0.5, 1]
}
},
encode: {
x: 'time',
y: 'value',
color: 'type'
}
};
纹理
// https://g2plot.antv.vision/zh/examples/plugin/pattern#bar-pattern
const data = [
{ type: '分类一', value: 27 },
{ type: '分类二', value: 25 },
{ type: '分类三', value: 18 },
{ type: '分类四', value: 15 },
{ type: '分类五', value: 10 },
];
const options = {
type: 'interval',
data,
coordinate: [{type: 'transpose'}],
scale: {
color: {
palette: {type: 'pattern'},
range: [
{type: 'dot'},
{type: 'line'},
{type: 'square'},
{
type: 'line',
spacing: 6,
lineWidth: 2,
rotation: 90,
},
{
type: 'square',
size: 5,
padding: 2,
rotation: 45,
isStagger: false,
}
]
}
},
encode: {
x: 'type',
y: 'value',
color: 'type' // 纹理也是通过 color 来指定
}
}
不同颜色方案
可以参考这个里面的颜色方案:https://github.com/antvis/color-schema。这个地方可能就需要人来整理 antv 对颜色方法,沉淀到 color-schema 这个库中,这个库目前完全不能用。
// https://g2.antv.vision/zh/examples/pie/basic#labelline
const data = [
{ item: '事例一', count: 40},
{ item: '事例二', count: 21},
{ item: '事例三', count: 17},
{ item: '事例四', count: 13},
{ item: '事例五', count: 9},
];
const options = {
type: 'interval',
statistic: [{type: 'percentageY'}, {type: 'stackY'}],
coordinate: [{type: 'polar'}],
scale: {
color: {
// 换一种配色
// 等同于修改默认的 range
// 上面图表理论上会变颜色
palette: {type: 'monochromatic'}
}
},
ecnode: {
y: 'count',
color: 'item'
}
}
智能颜色
使用这个库:https://github.com/antvis/smart-color,比如下面的从图片提取颜色的能力,但这个能力应该是通过插件的形式透出。
import {createRuntime, createLibrary} from '@antv/g2';
import {extract} from '@antv/smart-color';
// 注册
const ImagePalette = (options) => {
const { url } = options;
return () => {
return extract(url); // 返回一个颜色数组
}
};
ImagePalette.props = {
type: 'image',
};
const library = Object.assgin(createLibrary(), {'palette.image': ImagePalette});
const runtime = createRuntime({container},library);
const data = [
{ item: '事例一', count: 40},
{ item: '事例二', count: 21},
{ item: '事例三', count: 17},
{ item: '事例四', count: 13},
{ item: '事例五', count: 9},
];
const options = {
type: 'interval',
statistic: [{type: 'percentageY'}, {type: 'stackY'}],
coordinate: [{type: 'polar'}],
scale: {
color: {
palette: {type: 'image', url: 'xxx'}
}
},
ecnode: {
y: 'count',
color: 'item'
}
}
视图复合
flex
const options = {
type: 'flex',
direction: 'col',
data,
flex: [1, 1],
encode: {
x: 'time',
y: 'value;
},
children: [
{type: 'area'},
{
type: 'flex',
direction: 'col',
flex: [1, 1],
axis: false,
children: [
{type: 'interval'},
{type: 'area'}
]
}
]
};
分面
矩形分面
默认同步 data domain
// https://vega.github.io/vega-lite/examples/trellis_bar.html
const options = {
type: 'rect',
data,
encode: {
y: 'gender',
},
// 如果没有下面的注释的话就不同同步 data domain
// 这样上面 y 轴的范围将是不一样的
// sync 的时候是给孩子增加一个 statistic 去 filter index
// 不 sync 的时候是给孩子一个 transform 去 filter data
// sync: {
// data: false
// },
children: [
{
type: 'interval',
encode: {
x: 'age',
y: 'population'
}
}
],
};
带背景的矩形分面
// https://observablehq.com/@observablehq/plot-facets?collection=@observablehq/plot
const options = {
type: 'rect',
data,
encode: {
x: 'sex',
y: 'species'
},
children: [
{
type: 'layer',
encode: {
x: 'culmen_depth_mm',
y: 'culmen_length_mm'
},
children: [
{
type: 'point',
// 不过滤数据
// 实现背景非常的有效
filter: false
},
{type: 'point'}
]
}
]
};
矩阵分面
// https://vega.github.io/editor/#/examples/vega/brushing-scatter-plots
const response = await fetch('https://vega.github.io/editor/data/penguins.json');
const data = response.json();
// 会自动的生成一系列标准的配置
const options = {
type: 'matrix',
data,
component: [
{type: 'legendCategory', channel: 'color', position: 'right'}
],
fields: [
'Break Length (mm)',
'Break Depth (mm)',
'Flipper Length (mm)',
'Body Mass (g)'
],
children: [
{
type: 'point',
encode: {
// facet 元素会自动的生成该元素的 x 和 y encode
// 这个地方就不会 filter 了
color: 'Species'
}
}
]
};
带回调的矩阵分面
// https://g2.antv.vision/zh/examples/facet/facet#matrix
const options = {
type: 'matrix',
data,
fields: ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth'],
children: (props) => {
const {data, rowIndex, columnIndex, rowField, colField} = props;
if (rowIndex === columnIndex) {
return {
type: 'polygon',
data,
statistic: [
{type: 'binX'},
{type: 'summaryX', aggregate: 'count'}
],
encode: {
y: 'count',
x: colField,
color: 'Species'
}
}
} else {
return {
type: 'point',
data,
statistic: [{type: 'stackY'}],
encode: {
x: rowField,
y: colField,
color: 'Species',
}
}
}
}
};
树状分面
// https://g2.antv.vision/zh/examples/facet/facet#tree-column
const data = [
{ gender: '男', count: 40, class: '一班', grade: '一年级' },
{ gender: '女', count: 30, class: '一班', grade: '一年级' },
{ gender: '男', count: 35, class: '二班', grade: '一年级' },
{ gender: '女', count: 45, class: '二班', grade: '一年级' },
{ gender: '男', count: 20, class: '三班', grade: '一年级' },
{ gender: '女', count: 35, class: '三班', grade: '一年级' },
{ gender: '男', count: 30, class: '一班', grade: '二年级' },
{ gender: '女', count: 40, class: '一班', grade: '二年级' },
{ gender: '男', count: 25, class: '二班', grade: '二年级' },
{ gender: '女', count: 32, class: '二班', grade: '二年级' },
{ gender: '男', count: 28, class: '三班', grade: '二年级' },
{ gender: '女', count: 36, class: '三班', grade: '二年级' }
];
const options = {
type: 'tree',
data,
fields: ['grade', 'class'],
// 会默认生成 edge 和 text geometry
// 会被包裹成一个 layer container
children: [
{
type: 'interval',
statistic: [{type: 'stackY'}],
encode: {
x: 'percent',
color: 'gender',
}
}
]
};
组件
标题
const options = {
type: 'interval',
data,
encode: {
x: 'a',
y: 'b'
},
component: [
{type: 'title', content: 'A Simple Bar Chart'},
],
}
合并图例
const options = {
type: 'point',
data,
encode: {
x: 'Flipper Lengh (mm)',
y: 'Body Mass (g)',
color: 'Species',
shape: 'Species'
}
}
统计
选择
const options = {
type: 'layer',
encode: {
x:'Date',
y:'Close',
series: 'Symbol'
},
children: [
{
type: 'text',
statistic: [{type: 'selectLast'}],
},
{
type: 'line',
encode: {
color: 'Symbol'
}
}
]
};
总结
// https://g2.antv.vision/zh/examples/component/label#line2
const data = [
{
"date": "2012-09",
"buyin": 7228
},
// ...
]
const options = {
type: 'layer',
data,
children: [
{
type: 'layer',
encode: {
x: 'date',
y: 'buyin'
},
children: [
{ type: 'line' },
{
type: 'text',
encode: {
text: 'buyin'
}
}
]
},
{
type: 'annotation.line', // 新的 line
statistic: [
{type: 'summaryY', aggregate: 'mean'}
],
encode: {
y: 'mean'
}
}
]
}
分箱
const options = {
type: 'polygon',
statistic: [
{type: 'bin', shape:'rect'},
{type: 'summary', aggregate: 'count'}
],
encode: {
x: 'rainfall',
y: 'degdays',
// 这是一个 lazy encode
// 它的值是在 transform 完成之后才提取出来的
color: 'count'
}
}
const options = {
type: 'rect',
statistic: [
{type: 'binX'},
{type: 'summary', aggregate:'count'} // 可以省略 count
],
encode: {
x: 'weight',
y: 'count',
color: 'sex',
}
}
动画
更多案例:https://hanabi.data-viz.cn/templates?lang=zh-CN
条竞赛图
// https://observablehq.com/@d3/bar-chart-race-explained
const options = {
type: 'sequnce',
scale: {
timming: {
range: [0, 60 * 1000] // 一共执行时间是 1分钟
}
},
encode: {
timing: 'date'
},
children: [
{
type: 'text',
statistic: [
{type: 'selectLast'}
],
scale: {
},
encode: {
key: 'date',
text: d => d.date.getFullYear(),
x: 1,
y: 1
},
style: {
textAnchor: 'end',
fontSize: 50
}
},
{
type: 'interval',
coordinate: [{type: 'transpose'}],
statistic: [
// 只展示前 12 条数据
{type: 'sort', by: 'value'},
{type: 'filterRank', count: 12}
],
encode: {
key: 'name',
x: 'name',
y: 'value',
color: 'category'
}
}
]
};
数据实时更新
https://www.highcharts.com.cn/demo/highcharts/dynamic-update/dark-unica
甘特图动画
// https://canisjs.github.io/canis-editor/index.html?exmp=gantt_1
const data = [
{ eventStartTime: '', eventDurationTime: '', eventName: ''},
// ...
];
const config = {
type: 'interval',
data,
encode: {
x: 'eventName',
y: ['eventStartTime', d => d.eventStartTime + d.eventDurationTime],
enterDelay: 'eventStartTime', // 动画属性也和通道绑定
enterDuration: 'eventDurationTime' // 动画属性也和通道绑定
},
}
条形图动画(New)
// https://canisjs.github.io/canis-editor/index.html?exmp=groupedBar_1
const data = [
{year: '', sale: '', type: ''}
];
const options = {
data,
encode: {
x: 'year',
y: 'sale',
color: 'type',
enterDelay: 'type',
},
animte: {
enterDuration: 100,
enterType: '',
}
};
折线图尾随动画
const options = {
type: 'layer',
data,
children: [
{
type: 'line',
encode: {
x: 'year',
y: 'value',
color: 'type'
},
animate: {
enterType: 'path-in',
enterDuration: 10 * 1000,
}
},
{
type: 'sequnce',
scale: {
timing: {
range: [0, 10 * 1000]
}
},
encode: {
timing: 'year'
},
}
]
};
简单可视化叙事
这个地方应该再加上坐标轴的动画。
// https://echarts.apache.org/examples/zh/editor.html?c=scatter-aggregate-bar
// https://www.w3schools.com/css/css3_animations.asp
const data = [
{ height: '', weight: '', sex: 'male', name: '' },
// ...
];
const options = {
type: 'keyframe',
data,
iterationCount: 'infinite',
direction: 'alternate',
duration: '3000',
children: [
{
type: 'interval',
statistic: [
{type: 'groupX'},
{type: 'summary', aggregate: 'mean'}
],
encode: {
x: 'sex',
y: 'mean',
color: 'blue',
key: 'sex'
}
},
{
type: 'point',
encode: {
x: 'height',
y: 'weight',
color: 'sex',
key: 'name',
groupKey: 'sex'
}
}
],
};
普通可视化叙事 (Todo)
// https://stackblitz.com/github/pissang/echarts-www-landing-animation
复杂可视化叙事 (Todo)
// https://bl.ocks.org/mbostock/1256572
单元可视化
泰坦尼克(一)
关键有几点:
- 分 data domain 和 space domain
- 兄弟节点是否共享(同步)data domain 和 space domain
- 防止重叠的算法:pack
const options = {
type: 'rect',
data,
encode: {
x: 'Class',
y: 'Gender',
},
children: [
{
type: 'point',
ajust: [
{type: 'pack'} // 使用默认的大小 uniform 的
],
encode: {
color: 'Survived',
},
}
]
};
泰坦尼克(二)
const options = {
type: 'rect',
encode: {
x: 'Class'
},
children: [
{
type: 'point',
adjust: [{type: 'pack'}],
encode: {
size: 'ticket',
color: 'Survived'
}
}
]
};
泰坦尼克(三)
// (b)
const options = {
type: 'rect',
encode: {
x: 'Class',
},
sync: {
data: false // 不同步 data domain
},
children: [
{
type: 'point',
adjust: [{type: 'pack'}],
encode: {
color: 'Survived',
}
}
]
};
泰坦尼克(四)
const options = {
type: 'rect',
encode: {
x: 'survived',
y: 'pclass',
},
children: [
{
type: 'rect',
encode: {
y: 'sex',
},
children: [
{
type: 'point',
adjust: [{ type: 'pack' }],
encode: {
color: 'survived',
}
}
]
}
]
}
Sand Dance(Todo)
交互
有的交互修改数据,有的交互修改状态。框选
```javascript // https://g2.antv.vision/zh/examples/interaction/brush#brush-filter-record
function Brush() { return () => ({ showEnable: [ { trigger: ‘plot:mouseenter’, action: ‘cursor:crosshair’ }, { trigger: ‘mask:mouseenter’, action: ‘cursor:move’ }, { trigger: ‘plot:mouseleave’, action: ‘cursor:default’ }, { trigger: ‘mask:mouseleave’, action: ‘cursor:crosshair’ }, ], start: [ { trigger: ‘plot:mousedown’, isEnable(context) { return !context.isInShape(‘mask’); }, action: [‘rect-mask:start’, ‘rect-mask:show’] }, { trigger: ‘mask:dragstart’, action: ‘rect-mask:moveStart’ } ], processing: [ { trigger: ‘plot:mousemove’, action: ‘rect-mask:resize’ }, { trigger: ‘mask:drag’, isEnable(context) { return context.isInPlot(); }, action: ‘rect-mask:move’ }, { trigger: ‘mask:change’, action: ‘element-sibling-filter-record:filter’ } ], end: [ { trigger: ‘plot:mouseup’, action: ‘rect-mask:end’ }, { trigger: ‘mask:dragend’, action: ‘rect-mask:moveEnd’ } ], rollback: [ { trigger: ‘dblclick’, action: [‘rect-mask:hide’, ‘element-sibling-filter-record:reset’] } ] }); }
Brush.props = { name: ‘brush’ };
const options = { type: ‘flex’, data, children: [ { type: ‘point’, encode: { x: ‘carat’, y: ‘price’, }, interaction: [ { type: ‘brush’} ], }, { type: ‘point’, encode: { x: ‘depth’, y: ‘x’ } } ] };
<a name="Oyn2n"></a>
## 饼图转动
![image.png](https://cdn.nlark.com/yuque/0/2022/png/418707/1646391932530-0682d41e-a527-49e4-8f46-1d9e370bc871.png#clientId=uf3befbf1-0c05-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=229&id=u6dec7038&margin=%5Bobject%20Object%5D&name=image.png&originHeight=458&originWidth=658&originalType=binary&ratio=1&rotation=0&showTitle=false&size=55102&status=done&style=none&taskId=u1fe6c3b1-4528-4e56-ac05-bf5af62a2df&title=&width=329)
```javascript
// https://lark-assets-prod-aliyun.oss-accelerate.aliyuncs.com/lark/0/2021/gif/355/1636429547581-2de3143e-7a36-4775-bf2e-4dd6214576aa.gif?OSSAccessKeyId=LTAI4GGhPJmQ4HWCmhDAn4F5&Expires=1645519032&Signature=RO9%2F3LPuQkVWsRyCWrcufFtrUvQ%3D&response-content-disposition=inline
// https://intranetproxy.alipay.com/skylark/lark/0/2021/gif/355/1636429547581-2de3143e-7a36-4775-bf2e-4dd6214576aa.gif
// 注册一个新的 Action
function Rotate() {
// ...
}
// 注册一个新的 Action
function Select(options) {
const {x, y, eid, sid} = options;
return (runtime) => {
const context = runtime.getContext();
const view = getView(context, id); // 获得对应视图
const datum = getDatum(context, y); // 获得这个点的数据
const index = getIndex(context, datum); // 获得数据的索引
runtime.update([
sid, {index} // 更新对应的 idnex
]);
}
}
const options = {
type: 'layer',
data,
children: [
{
type: 'interval',
id: '$interval',
data,
statistic: [{type: 'stackY'}],
coordainte: [{type: 'polar', innerRadius: 0.5}],
encode: {
y: 'value',
color: 'type',
},
interaction: [{
type: 'on',
event: 'element:drag',
action: [
{type: 'rotate'},
{type: 'select', x: '50%', y: '10', eid: '$interval', sid: '$index'}
],
}],
},
{
type: 'layer',
statistic: [{type: 'selectIndex', id: '$index', index: 0}], // 定义一个 id
children: [
{
type: 'text',
encode: {
text: d => d.value + '人'
},
},
{
type: 'text',
encode: {
text: d => d.type
},
style: {
dy: '-1em'
}
}
]
},
// 视图树里面也可以有简单的 G 元素
// 和几何元素的区别在于和数据没有关联
// 不是数据驱动的
{
type: 'lineG',
style: {
x1: '50%', y1: 0,
x2: '50%', y2: 10
}
}
]
}
鱼眼
// https://g2.antv.vision/zh/examples/point/bubble#bubble
const options = {
type: 'point',
coordinate: [{type: 'fisheye', focusX: 0.5, focusY: 0.5, id: '$fisheye'}],
encode: {
x: 'GDP',
y: 'LifeExpectancy',
color: 'continent',
size: 'Population',
},
interaction: [
{
type: 'on', // 监听事件本身就是一个 interaction
trigger: 'plot:mousemove',
action: [{
// 说明返回值是配置的更新函描述
// 内部会重新调用 update 方法
type: 'runtime:update',
handler: (e) => {
const {x, y} = e.target;
// 更新 id 为 $fisheye 的配置
return [
'$fisheye', { focusX: x, focusY: y }
];
}
}]
}
],
};
// on 的内部大概实现
function On(options) {
const {trigger, action} = options
return () => {
return {
start: [{trigger, action}]
}
}
}
On.props = {
type: 'on'
};
自定义
图形
水波图
// https://g2plot.antv.vision/zh/examples/progress-plots/liquid#basic
import {createRuntime, createLibrary} from '@antv/g2';
const LiquidShape = (options) => {
const {shape} = options;
return (renderer, coordinate, attributes) => {
// draw liquid
// animation
}
};
LiquidShape.props = {
type: 'liquid',
metadata: {
geometry: 'interval',
},
};
const library = Object.assgin(createLibrary(), {'shape.liquild': LiquidShape});
const runtime = createRuntime({container}, library);
const options = {
type: 'layer',
data: [{value: 0.25}],
children: [
{
type: 'text',
encode: {
x: 0.5,
y: 0.5,
text: d => (d * 100).toFixed(2) + '%',
}
},
{
type: 'interval',
scale: {
y: {domain: [0, 1]}
},
encode: {
y: 'value',
shape: 'liquid'
}
}
],
};
连接不同视图
// https://g2.antv.vision/zh/examples/case/pie#pie2