几何元素

Interval

条形图

image.png

  1. // https://g2.antv.vision/zh/examples/column/basic#basic
  2. const data = [
  3. { year: '1951 年', sales: 38 },
  4. { year: '1952 年', sales: 52 },
  5. { year: '1956 年', sales: 61 },
  6. { year: '1957 年', sales: 145 },
  7. { year: '1958 年', sales: 48 },
  8. { year: '1959 年', sales: 38 },
  9. { year: '1960 年', sales: 38 },
  10. { year: '1962 年', sales: 38 },
  11. ];
  12. const options = {
  13. type: 'interval',
  14. data,
  15. encode: {
  16. x: 'year',
  17. y: 'scales'
  18. }
  19. };

区间条形图

image.png

  1. // https://g2.antv.vision/zh/examples/column/basic#ranged
  2. const data = [
  3. { x: '分类一', y1: 76, y2: 100 },
  4. { x: '分类二', y1: 56, y2: 108 },
  5. { x: '分类三', y1: 38, y2: 129 },
  6. { x: '分类四', y1: 58, y2: 155 },
  7. { x: '分类五', y1: 45, y2: 120 },
  8. { x: '分类六', y1: 23, y2: 99 },
  9. { x: '分类七', y1: 18, y2: 56 },
  10. { x: '分类八', y1: 18, y2: 34 },
  11. ];
  12. const options = {
  13. type: 'interval',
  14. data,
  15. encode: {
  16. x: 'x',
  17. y: ['y1', 'y2']
  18. }
  19. }

堆积条形图

image.png

  1. // https://g2.antv.vision/zh/examples/column/stack#stacked
  2. const data = [
  3. { name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 },
  4. { name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 },
  5. { name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 },
  6. { name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 },
  7. { name: 'London', 月份: 'May', 月均降雨量: 47 },
  8. { name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 },
  9. { name: 'London', 月份: 'Jul.', 月均降雨量: 24 },
  10. { name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 },
  11. { name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 },
  12. { name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 },
  13. { name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 },
  14. { name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 },
  15. { name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 },
  16. { name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 },
  17. { name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 },
  18. { name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 },
  19. ];
  20. const options = {
  21. type: 'interval',
  22. data,
  23. statistic: [{type: 'stackY'}],
  24. encode: {
  25. x: '月份',
  26. y: '月均降雨量',
  27. color: 'name',
  28. }
  29. }

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’, } }

  1. <a name="nqFVT"></a>
  2. ### 分组条形图
  3. ![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)
  4. ```javascript
  5. // https://g2.antv.vision/zh/examples/column/stack#stacked
  6. const data = [
  7. { name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 },
  8. { name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 },
  9. { name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 },
  10. { name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 },
  11. { name: 'London', 月份: 'May', 月均降雨量: 47 },
  12. { name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 },
  13. { name: 'London', 月份: 'Jul.', 月均降雨量: 24 },
  14. { name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 },
  15. { name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 },
  16. { name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 },
  17. { name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 },
  18. { name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 },
  19. { name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 },
  20. { name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 },
  21. { name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 },
  22. { name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 },
  23. ];
  24. const options = {
  25. type: 'interval',
  26. data,
  27. statistic: [{type: 'dodgeX'}], // dodgeX 推断出 series
  28. encode: {
  29. x: '月份',
  30. y: '月均降雨量',
  31. color: 'name',
  32. }
  33. }
  34. // 下面的写法也是可以的
  35. const options = {
  36. type: 'interval',
  37. data,
  38. encode: {
  39. x: '月份',
  40. y: '月均降雨量',
  41. series: 'name',
  42. color: 'name',
  43. }
  44. }

饼图

image.png

  1. // https://g2.antv.vision/zh/examples/pie/basic#labelline
  2. const data = [
  3. { item: '事例一', count: 40},
  4. { item: '事例二', count: 21},
  5. { item: '事例三', count: 17},
  6. { item: '事例四', count: 13},
  7. { item: '事例五', count: 9},
  8. ];
  9. const options = {
  10. type: 'interval',
  11. statistic: [{type: 'percentageY'}, {type: 'stackY'}],
  12. coordinate: [{type: 'polar'}],
  13. ecnode: {
  14. y: 'count',
  15. color: 'item'
  16. }
  17. }

漏斗图

image.png image.png

  1. // https://g2.antv.vision/en/examples/funnel/funnel#pyramid
  2. const data = [
  3. { action: '浏览网站', pv: 50000 },
  4. { action: '放入购物车', pv: 35000 },
  5. { action: '生成订单', pv: 25000 },
  6. { action: '支付订单', pv: 15000 },
  7. { action: '完成交易', pv: 8000 },
  8. ];
  9. const options = {
  10. type: 'interval',
  11. statistic: [{ type: 'symmetryY' }],
  12. coordinate: [ { type: 'transpose'}, { type: 'reflectY' }],
  13. ecnode: {
  14. y: 'count',
  15. x: 'action',
  16. color: 'action',
  17. shape: 'funnel'
  18. }
  19. }

Area

河流图

image.png

  1. // https://g2.antv.vision/zh/examples/area/streamgraph#streamgraph
  2. const options = {
  3. type: 'area',
  4. statistic: [{ type: 'stackY' }, { type: 'symmetryY' }],
  5. encode: {
  6. x: 'year',
  7. y: 'count',
  8. }
  9. }

雷达图

image.png

  1. // https://g2.antv.vision/zh/examples/radar/radar#basic
  2. const users = [
  3. { item: 'Design', user: 'a', score: 70 },
  4. { item: 'Design', user: 'b', score: 30 },
  5. { item: 'Development', user: 'a', score: 60 },
  6. { item: 'Development', user: 'b', score: 70 },
  7. { item: 'Marketing', user: 'a', score: 50 },
  8. { item: 'Marketing', user: 'b', score: 60 },
  9. { item: 'Users', user: 'a', score: 40 },
  10. { item: 'Users', user: 'b', score: 50 },
  11. { item: 'Test', user: 'a', score: 60 },
  12. { item: 'Test', user: 'b', score: 70 },
  13. { item: 'Language', user: 'a', score: 70 },
  14. { item: 'Language', user: 'b', score: 50 },
  15. { item: 'Technology', user: 'a', score: 50 },
  16. { item: 'Technology', user: 'b', score: 40 },
  17. { item: 'Support', user: 'a', score: 30 },
  18. { item: 'Support', user: 'b', score: 40 },
  19. { item: 'Sales', user: 'a', score: 60 },
  20. { item: 'Sales', user: 'b', score: 40 },
  21. { item: 'UX', user: 'a', score: 50 },
  22. { item: 'UX', user: 'b', score: 60 },
  23. ];
  24. const options = {
  25. type: 'layer',
  26. data,
  27. coordinates: [{ type: 'polar' }],
  28. encode: {
  29. x: 'item',
  30. y: 'score',
  31. color: 'user'
  32. },
  33. style: {
  34. fillOpacity: 0.4,
  35. strokeOpacity: 0.4,
  36. },
  37. children: [
  38. {type: 'area'},
  39. {type: 'point'},
  40. ],
  41. }

Line

平行坐标系

image.png

  1. // https://vega.github.io/vega/examples/parallel-coordinates/
  2. const response = await fetch('https://vega.github.io/vega/data/cars.json');
  3. const data = await response.json();
  4. const options = {
  5. type: 'line',
  6. data,
  7. coordainte: [{type: 'parallel'}],
  8. component: [
  9. {type: 'axisY', channel: 'position[0]', title: 'HHH'}
  10. ],
  11. scale: {
  12. 'position[0]': { nice: true },
  13. },
  14. encode: {
  15. position: [
  16. 'Cylinders',
  17. 'Displacement',
  18. 'Weight_in_Lbs',
  19. 'Horsepower',
  20. 'Acceleration',
  21. 'Miles_per_Callon',
  22. 'Year',
  23. ],
  24. }
  25. }

Text

词云图

  1. // https://vega.github.io/vega/examples/word-cloud/
  2. const options = {
  3. type: 'text',
  4. data: '...',
  5. transform: [
  6. {type: 'split'},
  7. {type: 'wordcloud'} // 这是个异步的操作
  8. ],
  9. scale: {
  10. color: {
  11. range: ["#d5a928", "#652c90", "#939597"]
  12. }
  13. },
  14. encode: {
  15. text: 'text',
  16. x: 'x',
  17. y: 'y',
  18. fontSize: 'fontSize',
  19. rotation: 'rotation',
  20. color: 'text',
  21. },
  22. };

Annotation

image.png

  1. // https://g2.antv.vision/zh/examples/case/line#line7
  2. const options = {
  3. type: 'layer',
  4. data,
  5. encode: {
  6. x: 'Date',
  7. y: 'Close'
  8. },
  9. children: [
  10. {
  11. type: 'line'
  12. },
  13. {
  14. type: 'text',
  15. statistic: [
  16. {type: 'selectMinY'}
  17. ],
  18. encode: {
  19. text: d => `全部谷值:${d.Close}`
  20. }
  21. },
  22. {
  23. type: 'text',
  24. statistic: [
  25. {type: 'selectMaxY'}
  26. ],
  27. encode: {
  28. text: d => `全部峰值:${d.Close}`
  29. }
  30. }
  31. ],
  32. };

外部 Label

image.png

  1. // https://g2.antv.vision/zh/examples/case/column#column2
  2. const data = [
  3. { type: '未知', value: 654, percent: 0.02 },
  4. { type: '17 岁以下', value: 654, percent: 0.02 },
  5. { type: '18-24 岁', value: 4400, percent: 0.2 },
  6. { type: '25-29 岁', value: 5300, percent: 0.24 },
  7. { type: '30-39 岁', value: 6200, percent: 0.28 },
  8. { type: '40-49 岁', value: 3300, percent: 0.14 },
  9. { type: '50 岁以上', value: 1500, percent: 0.06 },
  10. ];
  11. // 这是其中一种写法,text,interval 是同一层级的东西
  12. const config = {
  13. type: 'layer',
  14. encode: {
  15. x: 'type',
  16. y: 'value'
  17. },
  18. children: [
  19. { type: 'interval'},
  20. {
  21. type: 'text',
  22. encode: { text: 'value' },
  23. style: { dy: '-2em' }
  24. },
  25. {
  26. type: 'text',
  27. encode: { text: 'percent' },
  28. style: { dy: '-1em' }
  29. }
  30. ],
  31. }
  32. // 这是第二种写法,text 是 interval 的一部分
  33. // 这样 text 就可以感知 interval 的 bounding box
  34. const options = {
  35. type: 'interval',
  36. data,
  37. encode: {
  38. x: 'type',
  39. y: 'value'
  40. },
  41. children: [
  42. {
  43. type: 'text', // 默认位置是父亲元素 bounding box 的左上角
  44. encode: { text: 'value' },
  45. style: { dy: '-2em' }
  46. },
  47. {
  48. type: 'text',
  49. encode: { text: 'percent' },
  50. style: { dy: '-1em' }
  51. }
  52. ]
  53. };

内部 Label

image.png

  1. // https://g2.antv.vision/zh/examples/relation/relation#treemap
  2. const options = {
  3. type: 'polygan',
  4. data,
  5. transform: [
  6. {type: 'treemap'}
  7. ],
  8. encode: {
  9. x: 'x',
  10. y: 'y'
  11. },
  12. children: [
  13. {
  14. type: 'text',
  15. encode: {
  16. x: 0.5, // 指定为中心
  17. y: 0.5, // 指定为中心
  18. text: 'label',
  19. },
  20. // scale: {
  21. // x: {type: 'identity'},
  22. // y: {type: 'identity'}
  23. // }
  24. }
  25. ]
  26. }

Image

散点图

image.png

  1. // https://g2.antv.vision/zh/examples/point/bubble#bubble-image
  2. const data = [
  3. { name: 'Internet Explorer', value: 26, imageURL: ''},
  4. { name: 'Chrome', value: 40, imageURL: ''},
  5. { name: 'Firefox', value: 30, imageURL: ''},
  6. { name: 'Safari', value: 24, imageURL: ''},
  7. { name: 'Opera', value: 15, imageURL: ''},
  8. { name: 'Undetectable', value: 8, imageURL: ''}
  9. ];
  10. const options = {
  11. type: 'layer',
  12. children: [
  13. {
  14. type: 'edge',
  15. x: ['name', 'name'],
  16. y: ['value', 1],
  17. shape: 'dash'
  18. },
  19. {
  20. type: 'image',
  21. data,
  22. scale: {
  23. size: {type: 'pow'}
  24. },
  25. encode: {
  26. x: 'name',
  27. y: 'value',
  28. url: 'imageURL',
  29. size: 'value'
  30. }
  31. }
  32. ]
  33. };

Mix

桑基图

image.png

  1. // https://g2.antv.vision/zh/examples/case/sankey/#sankey-card
  2. const options = {
  3. type: 'layer',
  4. data,
  5. transform: [
  6. { type: 'sankey', /** 更多参数 **/ } // 在这里计算
  7. ],
  8. children: [
  9. {
  10. transform: [{type: 'pick', fields: ['nodes']}],
  11. type: 'polygon',
  12. encode: {
  13. x: 'x',
  14. y: 'y,
  15. }
  16. },
  17. {
  18. transform: [{type: 'pick', fields: ['edges']}],
  19. type: 'edge',
  20. encode: {
  21. shape: 'arc',
  22. x: 'x',
  23. y: 'y'
  24. }
  25. }
  26. ]
  27. };

双轴柱线图

image.png

  1. // https://g2.antv.vision/zh/examples/other/other#double-axes
  2. const data = [
  3. { time: '10:10', call: 4, waiting: 2, people: 2 },
  4. { time: '10:15', call: 2, waiting: 6, people: 3 },
  5. { time: '10:20', call: 13, waiting: 2, people: 5 },
  6. { time: '10:25', call: 9, waiting: 9, people: 1 },
  7. { time: '10:30', call: 5, waiting: 2, people: 3 },
  8. { time: '10:35', call: 8, waiting: 2, people: 1 },
  9. { time: '10:40', call: 13, waiting: 1, people: 2 }
  10. ];
  11. const options = {
  12. type: 'layer',
  13. data,
  14. encode: {x: 'time'},
  15. sync: {
  16. y: false
  17. },
  18. children: [
  19. {
  20. type: 'interval',
  21. encode: {
  22. y: 'waiting',
  23. color: '#3182bd',
  24. }
  25. },
  26. {
  27. type: 'layer',
  28. encode: {
  29. y: 'people',
  30. color: '#fdae6b'
  31. },
  32. component: [
  33. // 没有这条声明轴会都在右边
  34. // 因为这两者的 scale 没有同步
  35. {type: 'axisY', position: 'left'}
  36. ],
  37. children: [
  38. {type: 'point'},
  39. {type: 'line'}
  40. ]
  41. }
  42. ],
  43. };

image.png
这种情况可能需要一种同步两边刻度的算法,但是只能保证一边刻度的可读性。

柱状双轴图

image.png

  1. // https://codesandbox.io/s/dual-axes-of-column-5xk0pg?file=/index.ts
  2. const data = [
  3. { year: "1991", value: 3, count: 10 },
  4. { year: "1992", value: 4, count: 4 },
  5. { year: "1993", value: 3.5, count: 5 },
  6. { year: "1994", value: 5, count: 5 },
  7. { year: "1995", value: 4.9, count: 4.9 },
  8. { year: "1996", value: 6, count: 35 },
  9. { year: "1997", value: 7, count: 7 },
  10. { year: "1998", value: 9, count: 1 },
  11. { year: "1999", value: 13, count: 20 }
  12. ];
  13. const options = {
  14. type: 'layer',
  15. data,
  16. sync: {
  17. y: false,
  18. },
  19. encode: {
  20. x: 'year',
  21. y: 'value',
  22. },
  23. scale: {
  24. series: {
  25. range: ['a', 'b'] // 指定一共有两个序列
  26. }
  27. },
  28. children: [
  29. {
  30. type: 'interval',
  31. encode: {
  32. series: 'a', // 指定是序列 a
  33. }
  34. },
  35. {
  36. type: 'interval',
  37. encode: {
  38. series: 'b', // 指定是序列 b
  39. }
  40. }
  41. ]
  42. };

比例尺

分段映射

暂时用双轴图代替
image.png

  1. // https://github.com/antvis/G2/issues/2712
  2. const options = {
  3. type: 'line',
  4. data,
  5. scale: {
  6. y: {
  7. domain: [0, 80, 100, 105],
  8. range: [0, 0.5, 0.5, 1]
  9. }
  10. },
  11. encode: {
  12. x: 'time',
  13. y: 'value',
  14. color: 'type'
  15. }
  16. };

纹理

image.png

  1. // https://g2plot.antv.vision/zh/examples/plugin/pattern#bar-pattern
  2. const data = [
  3. { type: '分类一', value: 27 },
  4. { type: '分类二', value: 25 },
  5. { type: '分类三', value: 18 },
  6. { type: '分类四', value: 15 },
  7. { type: '分类五', value: 10 },
  8. ];
  9. const options = {
  10. type: 'interval',
  11. data,
  12. coordinate: [{type: 'transpose'}],
  13. scale: {
  14. color: {
  15. palette: {type: 'pattern'},
  16. range: [
  17. {type: 'dot'},
  18. {type: 'line'},
  19. {type: 'square'},
  20. {
  21. type: 'line',
  22. spacing: 6,
  23. lineWidth: 2,
  24. rotation: 90,
  25. },
  26. {
  27. type: 'square',
  28. size: 5,
  29. padding: 2,
  30. rotation: 45,
  31. isStagger: false,
  32. }
  33. ]
  34. }
  35. },
  36. encode: {
  37. x: 'type',
  38. y: 'value',
  39. color: 'type' // 纹理也是通过 color 来指定
  40. }
  41. }

不同颜色方案

可以参考这个里面的颜色方案:https://github.com/antvis/color-schema。这个地方可能就需要人来整理 antv 对颜色方法,沉淀到 color-schema 这个库中,这个库目前完全不能用。
image.png

  1. // https://g2.antv.vision/zh/examples/pie/basic#labelline
  2. const data = [
  3. { item: '事例一', count: 40},
  4. { item: '事例二', count: 21},
  5. { item: '事例三', count: 17},
  6. { item: '事例四', count: 13},
  7. { item: '事例五', count: 9},
  8. ];
  9. const options = {
  10. type: 'interval',
  11. statistic: [{type: 'percentageY'}, {type: 'stackY'}],
  12. coordinate: [{type: 'polar'}],
  13. scale: {
  14. color: {
  15. // 换一种配色
  16. // 等同于修改默认的 range
  17. // 上面图表理论上会变颜色
  18. palette: {type: 'monochromatic'}
  19. }
  20. },
  21. ecnode: {
  22. y: 'count',
  23. color: 'item'
  24. }
  25. }

智能颜色

使用这个库:https://github.com/antvis/smart-color,比如下面的从图片提取颜色的能力,但这个能力应该是通过插件的形式透出。

  1. import {createRuntime, createLibrary} from '@antv/g2';
  2. import {extract} from '@antv/smart-color';
  3. // 注册
  4. const ImagePalette = (options) => {
  5. const { url } = options;
  6. return () => {
  7. return extract(url); // 返回一个颜色数组
  8. }
  9. };
  10. ImagePalette.props = {
  11. type: 'image',
  12. };
  13. const library = Object.assgin(createLibrary(), {'palette.image': ImagePalette});
  14. const runtime = createRuntime({container},library);
  15. const data = [
  16. { item: '事例一', count: 40},
  17. { item: '事例二', count: 21},
  18. { item: '事例三', count: 17},
  19. { item: '事例四', count: 13},
  20. { item: '事例五', count: 9},
  21. ];
  22. const options = {
  23. type: 'interval',
  24. statistic: [{type: 'percentageY'}, {type: 'stackY'}],
  25. coordinate: [{type: 'polar'}],
  26. scale: {
  27. color: {
  28. palette: {type: 'image', url: 'xxx'}
  29. }
  30. },
  31. ecnode: {
  32. y: 'count',
  33. color: 'item'
  34. }
  35. }

视图复合

flex

image.png

  1. const options = {
  2. type: 'flex',
  3. direction: 'col',
  4. data,
  5. flex: [1, 1],
  6. encode: {
  7. x: 'time',
  8. y: 'value;
  9. },
  10. children: [
  11. {type: 'area'},
  12. {
  13. type: 'flex',
  14. direction: 'col',
  15. flex: [1, 1],
  16. axis: false,
  17. children: [
  18. {type: 'interval'},
  19. {type: 'area'}
  20. ]
  21. }
  22. ]
  23. };

分面

矩形分面

默认同步 data domain
image.png

  1. // https://vega.github.io/vega-lite/examples/trellis_bar.html
  2. const options = {
  3. type: 'rect',
  4. data,
  5. encode: {
  6. y: 'gender',
  7. },
  8. // 如果没有下面的注释的话就不同同步 data domain
  9. // 这样上面 y 轴的范围将是不一样的
  10. // sync 的时候是给孩子增加一个 statistic 去 filter index
  11. // 不 sync 的时候是给孩子一个 transform 去 filter data
  12. // sync: {
  13. // data: false
  14. // },
  15. children: [
  16. {
  17. type: 'interval',
  18. encode: {
  19. x: 'age',
  20. y: 'population'
  21. }
  22. }
  23. ],
  24. };

带背景的矩形分面

image.png

  1. // https://observablehq.com/@observablehq/plot-facets?collection=@observablehq/plot
  2. const options = {
  3. type: 'rect',
  4. data,
  5. encode: {
  6. x: 'sex',
  7. y: 'species'
  8. },
  9. children: [
  10. {
  11. type: 'layer',
  12. encode: {
  13. x: 'culmen_depth_mm',
  14. y: 'culmen_length_mm'
  15. },
  16. children: [
  17. {
  18. type: 'point',
  19. // 不过滤数据
  20. // 实现背景非常的有效
  21. filter: false
  22. },
  23. {type: 'point'}
  24. ]
  25. }
  26. ]
  27. };

矩阵分面

image.png

  1. // https://vega.github.io/editor/#/examples/vega/brushing-scatter-plots
  2. const response = await fetch('https://vega.github.io/editor/data/penguins.json');
  3. const data = response.json();
  4. // 会自动的生成一系列标准的配置
  5. const options = {
  6. type: 'matrix',
  7. data,
  8. component: [
  9. {type: 'legendCategory', channel: 'color', position: 'right'}
  10. ],
  11. fields: [
  12. 'Break Length (mm)',
  13. 'Break Depth (mm)',
  14. 'Flipper Length (mm)',
  15. 'Body Mass (g)'
  16. ],
  17. children: [
  18. {
  19. type: 'point',
  20. encode: {
  21. // facet 元素会自动的生成该元素的 x 和 y encode
  22. // 这个地方就不会 filter 了
  23. color: 'Species'
  24. }
  25. }
  26. ]
  27. };

带回调的矩阵分面

image.png

  1. // https://g2.antv.vision/zh/examples/facet/facet#matrix
  2. const options = {
  3. type: 'matrix',
  4. data,
  5. fields: ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth'],
  6. children: (props) => {
  7. const {data, rowIndex, columnIndex, rowField, colField} = props;
  8. if (rowIndex === columnIndex) {
  9. return {
  10. type: 'polygon',
  11. data,
  12. statistic: [
  13. {type: 'binX'},
  14. {type: 'summaryX', aggregate: 'count'}
  15. ],
  16. encode: {
  17. y: 'count',
  18. x: colField,
  19. color: 'Species'
  20. }
  21. }
  22. } else {
  23. return {
  24. type: 'point',
  25. data,
  26. statistic: [{type: 'stackY'}],
  27. encode: {
  28. x: rowField,
  29. y: colField,
  30. color: 'Species',
  31. }
  32. }
  33. }
  34. }
  35. };

树状分面

image.png

  1. // https://g2.antv.vision/zh/examples/facet/facet#tree-column
  2. const data = [
  3. { gender: '男', count: 40, class: '一班', grade: '一年级' },
  4. { gender: '女', count: 30, class: '一班', grade: '一年级' },
  5. { gender: '男', count: 35, class: '二班', grade: '一年级' },
  6. { gender: '女', count: 45, class: '二班', grade: '一年级' },
  7. { gender: '男', count: 20, class: '三班', grade: '一年级' },
  8. { gender: '女', count: 35, class: '三班', grade: '一年级' },
  9. { gender: '男', count: 30, class: '一班', grade: '二年级' },
  10. { gender: '女', count: 40, class: '一班', grade: '二年级' },
  11. { gender: '男', count: 25, class: '二班', grade: '二年级' },
  12. { gender: '女', count: 32, class: '二班', grade: '二年级' },
  13. { gender: '男', count: 28, class: '三班', grade: '二年级' },
  14. { gender: '女', count: 36, class: '三班', grade: '二年级' }
  15. ];
  16. const options = {
  17. type: 'tree',
  18. data,
  19. fields: ['grade', 'class'],
  20. // 会默认生成 edge 和 text geometry
  21. // 会被包裹成一个 layer container
  22. children: [
  23. {
  24. type: 'interval',
  25. statistic: [{type: 'stackY'}],
  26. encode: {
  27. x: 'percent',
  28. color: 'gender',
  29. }
  30. }
  31. ]
  32. };

组件

标题

image.png

  1. const options = {
  2. type: 'interval',
  3. data,
  4. encode: {
  5. x: 'a',
  6. y: 'b'
  7. },
  8. component: [
  9. {type: 'title', content: 'A Simple Bar Chart'},
  10. ],
  11. }

合并图例

image.png

  1. const options = {
  2. type: 'point',
  3. data,
  4. encode: {
  5. x: 'Flipper Lengh (mm)',
  6. y: 'Body Mass (g)',
  7. color: 'Species',
  8. shape: 'Species'
  9. }
  10. }

统计

选择

image.png

  1. const options = {
  2. type: 'layer',
  3. encode: {
  4. x:'Date',
  5. y:'Close',
  6. series: 'Symbol'
  7. },
  8. children: [
  9. {
  10. type: 'text',
  11. statistic: [{type: 'selectLast'}],
  12. },
  13. {
  14. type: 'line',
  15. encode: {
  16. color: 'Symbol'
  17. }
  18. }
  19. ]
  20. };

总结

image.png

  1. // https://g2.antv.vision/zh/examples/component/label#line2
  2. const data = [
  3. {
  4. "date": "2012-09",
  5. "buyin": 7228
  6. },
  7. // ...
  8. ]
  9. const options = {
  10. type: 'layer',
  11. data,
  12. children: [
  13. {
  14. type: 'layer',
  15. encode: {
  16. x: 'date',
  17. y: 'buyin'
  18. },
  19. children: [
  20. { type: 'line' },
  21. {
  22. type: 'text',
  23. encode: {
  24. text: 'buyin'
  25. }
  26. }
  27. ]
  28. },
  29. {
  30. type: 'annotation.line', // 新的 line
  31. statistic: [
  32. {type: 'summaryY', aggregate: 'mean'}
  33. ],
  34. encode: {
  35. y: 'mean'
  36. }
  37. }
  38. ]
  39. }

分箱

image.png

  1. const options = {
  2. type: 'polygon',
  3. statistic: [
  4. {type: 'bin', shape:'rect'},
  5. {type: 'summary', aggregate: 'count'}
  6. ],
  7. encode: {
  8. x: 'rainfall',
  9. y: 'degdays',
  10. // 这是一个 lazy encode
  11. // 它的值是在 transform 完成之后才提取出来的
  12. color: 'count'
  13. }
  14. }

image.png

  1. const options = {
  2. type: 'rect',
  3. statistic: [
  4. {type: 'binX'},
  5. {type: 'summary', aggregate:'count'} // 可以省略 count
  6. ],
  7. encode: {
  8. x: 'weight',
  9. y: 'count',
  10. color: 'sex',
  11. }
  12. }

动画

更多案例:https://hanabi.data-viz.cn/templates?lang=zh-CN

条竞赛图

image.png
image.png

  1. // https://observablehq.com/@d3/bar-chart-race-explained
  2. const options = {
  3. type: 'sequnce',
  4. scale: {
  5. timming: {
  6. range: [0, 60 * 1000] // 一共执行时间是 1分钟
  7. }
  8. },
  9. encode: {
  10. timing: 'date'
  11. },
  12. children: [
  13. {
  14. type: 'text',
  15. statistic: [
  16. {type: 'selectLast'}
  17. ],
  18. scale: {
  19. },
  20. encode: {
  21. key: 'date',
  22. text: d => d.date.getFullYear(),
  23. x: 1,
  24. y: 1
  25. },
  26. style: {
  27. textAnchor: 'end',
  28. fontSize: 50
  29. }
  30. },
  31. {
  32. type: 'interval',
  33. coordinate: [{type: 'transpose'}],
  34. statistic: [
  35. // 只展示前 12 条数据
  36. {type: 'sort', by: 'value'},
  37. {type: 'filterRank', count: 12}
  38. ],
  39. encode: {
  40. key: 'name',
  41. x: 'name',
  42. y: 'value',
  43. color: 'category'
  44. }
  45. }
  46. ]
  47. };

数据实时更新

https://www.highcharts.com.cn/demo/highcharts/dynamic-update/dark-unica

甘特图动画

Feb-21-2022 13-52-16.gif

  1. // https://canisjs.github.io/canis-editor/index.html?exmp=gantt_1
  2. const data = [
  3. { eventStartTime: '', eventDurationTime: '', eventName: ''},
  4. // ...
  5. ];
  6. const config = {
  7. type: 'interval',
  8. data,
  9. encode: {
  10. x: 'eventName',
  11. y: ['eventStartTime', d => d.eventStartTime + d.eventDurationTime],
  12. enterDelay: 'eventStartTime', // 动画属性也和通道绑定
  13. enterDuration: 'eventDurationTime' // 动画属性也和通道绑定
  14. },
  15. }

条形图动画(New)

Feb-23-2022 17-23-17.gif

  1. // https://canisjs.github.io/canis-editor/index.html?exmp=groupedBar_1
  2. const data = [
  3. {year: '', sale: '', type: ''}
  4. ];
  5. const options = {
  6. data,
  7. encode: {
  8. x: 'year',
  9. y: 'sale',
  10. color: 'type',
  11. enterDelay: 'type',
  12. },
  13. animte: {
  14. enterDuration: 100,
  15. enterType: '',
  16. }
  17. };

折线图尾随动画

Feb-18-2022 17-40-06.gif

  1. const options = {
  2. type: 'layer',
  3. data,
  4. children: [
  5. {
  6. type: 'line',
  7. encode: {
  8. x: 'year',
  9. y: 'value',
  10. color: 'type'
  11. },
  12. animate: {
  13. enterType: 'path-in',
  14. enterDuration: 10 * 1000,
  15. }
  16. },
  17. {
  18. type: 'sequnce',
  19. scale: {
  20. timing: {
  21. range: [0, 10 * 1000]
  22. }
  23. },
  24. encode: {
  25. timing: 'year'
  26. },
  27. }
  28. ]
  29. };

简单可视化叙事

Feb-21-2022 10-54-50.gif
这个地方应该再加上坐标轴的动画。

  1. // https://echarts.apache.org/examples/zh/editor.html?c=scatter-aggregate-bar
  2. // https://www.w3schools.com/css/css3_animations.asp
  3. const data = [
  4. { height: '', weight: '', sex: 'male', name: '' },
  5. // ...
  6. ];
  7. const options = {
  8. type: 'keyframe',
  9. data,
  10. iterationCount: 'infinite',
  11. direction: 'alternate',
  12. duration: '3000',
  13. children: [
  14. {
  15. type: 'interval',
  16. statistic: [
  17. {type: 'groupX'},
  18. {type: 'summary', aggregate: 'mean'}
  19. ],
  20. encode: {
  21. x: 'sex',
  22. y: 'mean',
  23. color: 'blue',
  24. key: 'sex'
  25. }
  26. },
  27. {
  28. type: 'point',
  29. encode: {
  30. x: 'height',
  31. y: 'weight',
  32. color: 'sex',
  33. key: 'name',
  34. groupKey: 'sex'
  35. }
  36. }
  37. ],
  38. };

普通可视化叙事 (Todo)

Feb-18-2022 17-29-50.gif

  1. // https://stackblitz.com/github/pissang/echarts-www-landing-animation

复杂可视化叙事 (Todo)

  1. // https://bl.ocks.org/mbostock/1256572

单元可视化

泰坦尼克(一)

image.png
关键有几点:

  • 分 data domain 和 space domain
  • 兄弟节点是否共享(同步)data domain 和 space domain
  • 防止重叠的算法:pack
    1. const options = {
    2. type: 'rect',
    3. data,
    4. encode: {
    5. x: 'Class',
    6. y: 'Gender',
    7. },
    8. children: [
    9. {
    10. type: 'point',
    11. ajust: [
    12. {type: 'pack'} // 使用默认的大小 uniform 的
    13. ],
    14. encode: {
    15. color: 'Survived',
    16. },
    17. }
    18. ]
    19. };

    泰坦尼克(二)

    image.png
    1. const options = {
    2. type: 'rect',
    3. encode: {
    4. x: 'Class'
    5. },
    6. children: [
    7. {
    8. type: 'point',
    9. adjust: [{type: 'pack'}],
    10. encode: {
    11. size: 'ticket',
    12. color: 'Survived'
    13. }
    14. }
    15. ]
    16. };

    泰坦尼克(三)

    image.png
    1. // (b)
    2. const options = {
    3. type: 'rect',
    4. encode: {
    5. x: 'Class',
    6. },
    7. sync: {
    8. data: false // 不同步 data domain
    9. },
    10. children: [
    11. {
    12. type: 'point',
    13. adjust: [{type: 'pack'}],
    14. encode: {
    15. color: 'Survived',
    16. }
    17. }
    18. ]
    19. };

    泰坦尼克(四)

    image.pngimage.png
    1. const options = {
    2. type: 'rect',
    3. encode: {
    4. x: 'survived',
    5. y: 'pclass',
    6. },
    7. children: [
    8. {
    9. type: 'rect',
    10. encode: {
    11. y: 'sex',
    12. },
    13. children: [
    14. {
    15. type: 'point',
    16. adjust: [{ type: 'pack' }],
    17. encode: {
    18. color: 'survived',
    19. }
    20. }
    21. ]
    22. }
    23. ]
    24. }

    Sand Dance(Todo)

    交互

    有的交互修改数据,有的交互修改状态。

    框选

    image.png ```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’ } } ] };

  1. <a name="Oyn2n"></a>
  2. ## 饼图转动
  3. ![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)
  4. ```javascript
  5. // 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
  6. // https://intranetproxy.alipay.com/skylark/lark/0/2021/gif/355/1636429547581-2de3143e-7a36-4775-bf2e-4dd6214576aa.gif
  7. // 注册一个新的 Action
  8. function Rotate() {
  9. // ...
  10. }
  11. // 注册一个新的 Action
  12. function Select(options) {
  13. const {x, y, eid, sid} = options;
  14. return (runtime) => {
  15. const context = runtime.getContext();
  16. const view = getView(context, id); // 获得对应视图
  17. const datum = getDatum(context, y); // 获得这个点的数据
  18. const index = getIndex(context, datum); // 获得数据的索引
  19. runtime.update([
  20. sid, {index} // 更新对应的 idnex
  21. ]);
  22. }
  23. }
  24. const options = {
  25. type: 'layer',
  26. data,
  27. children: [
  28. {
  29. type: 'interval',
  30. id: '$interval',
  31. data,
  32. statistic: [{type: 'stackY'}],
  33. coordainte: [{type: 'polar', innerRadius: 0.5}],
  34. encode: {
  35. y: 'value',
  36. color: 'type',
  37. },
  38. interaction: [{
  39. type: 'on',
  40. event: 'element:drag',
  41. action: [
  42. {type: 'rotate'},
  43. {type: 'select', x: '50%', y: '10', eid: '$interval', sid: '$index'}
  44. ],
  45. }],
  46. },
  47. {
  48. type: 'layer',
  49. statistic: [{type: 'selectIndex', id: '$index', index: 0}], // 定义一个 id
  50. children: [
  51. {
  52. type: 'text',
  53. encode: {
  54. text: d => d.value + '人'
  55. },
  56. },
  57. {
  58. type: 'text',
  59. encode: {
  60. text: d => d.type
  61. },
  62. style: {
  63. dy: '-1em'
  64. }
  65. }
  66. ]
  67. },
  68. // 视图树里面也可以有简单的 G 元素
  69. // 和几何元素的区别在于和数据没有关联
  70. // 不是数据驱动的
  71. {
  72. type: 'lineG',
  73. style: {
  74. x1: '50%', y1: 0,
  75. x2: '50%', y2: 10
  76. }
  77. }
  78. ]
  79. }

鱼眼

68747470733a2f2f67772e616c697061796f626a656374732e636f6d2f6d646e2f726d735f3032363636352f616674732f696d672f412a47356a49514c5251383651414141414141414141414141414152516e4151.gif

  1. // https://g2.antv.vision/zh/examples/point/bubble#bubble
  2. const options = {
  3. type: 'point',
  4. coordinate: [{type: 'fisheye', focusX: 0.5, focusY: 0.5, id: '$fisheye'}],
  5. encode: {
  6. x: 'GDP',
  7. y: 'LifeExpectancy',
  8. color: 'continent',
  9. size: 'Population',
  10. },
  11. interaction: [
  12. {
  13. type: 'on', // 监听事件本身就是一个 interaction
  14. trigger: 'plot:mousemove',
  15. action: [{
  16. // 说明返回值是配置的更新函描述
  17. // 内部会重新调用 update 方法
  18. type: 'runtime:update',
  19. handler: (e) => {
  20. const {x, y} = e.target;
  21. // 更新 id 为 $fisheye 的配置
  22. return [
  23. '$fisheye', { focusX: x, focusY: y }
  24. ];
  25. }
  26. }]
  27. }
  28. ],
  29. };
  30. // on 的内部大概实现
  31. function On(options) {
  32. const {trigger, action} = options
  33. return () => {
  34. return {
  35. start: [{trigger, action}]
  36. }
  37. }
  38. }
  39. On.props = {
  40. type: 'on'
  41. };

自定义

图形

水波图

image.png

  1. // https://g2plot.antv.vision/zh/examples/progress-plots/liquid#basic
  2. import {createRuntime, createLibrary} from '@antv/g2';
  3. const LiquidShape = (options) => {
  4. const {shape} = options;
  5. return (renderer, coordinate, attributes) => {
  6. // draw liquid
  7. // animation
  8. }
  9. };
  10. LiquidShape.props = {
  11. type: 'liquid',
  12. metadata: {
  13. geometry: 'interval',
  14. },
  15. };
  16. const library = Object.assgin(createLibrary(), {'shape.liquild': LiquidShape});
  17. const runtime = createRuntime({container}, library);
  18. const options = {
  19. type: 'layer',
  20. data: [{value: 0.25}],
  21. children: [
  22. {
  23. type: 'text',
  24. encode: {
  25. x: 0.5,
  26. y: 0.5,
  27. text: d => (d * 100).toFixed(2) + '%',
  28. }
  29. },
  30. {
  31. type: 'interval',
  32. scale: {
  33. y: {domain: [0, 1]}
  34. },
  35. encode: {
  36. y: 'value',
  37. shape: 'liquid'
  38. }
  39. }
  40. ],
  41. };

连接不同视图

image.png

  1. // https://g2.antv.vision/zh/examples/case/pie#pie2