移动端自适应

ECharts 工作在用户指定高宽的 DOM 节点(容器)中。ECharts 的『组件』和『系列』都在这个 DOM 节点中,每个节点都可以由用户指定位置。图表库内部并不适宜实现 DOM 文档流布局,因此采用类似绝对布局的简单容易理解的布局方式。但是有时候容器尺寸极端时,这种方式并不能自动避免组件重叠的情况,尤其在移动端小屏的情况下。

另外,有时会出现一个图表需要同时在PC、移动端上展现的场景。这需要 ECharts 内部组件随着容器尺寸变化而变化的能力。

为了解决这个问题,ECharts 完善了组件的定位设置,并且实现了类似 CSS Media Query 的自适应能力。

ECharts组件的定位和布局

大部分『组件』和『系列』会遵循两种定位方式:

left/right/top/bottom/width/height 定位方式:

这六个量中,每个量都可以是『绝对值』或者『百分比』或者『位置描述』。

  • 绝对值

    单位是浏览器像素(px),用 number 形式书写(不写单位)。例如 {left: 23, height: 400}

  • 百分比

    表示占 DOM 容器高宽的百分之多少,用 string 形式书写。例如 {right: '30%', bottom: '40%'}

  • 位置描述

    • 可以设置 left: 'center',表示水平居中。
    • 可以设置 top: 'middle',表示垂直居中。 这六个量的概念,和 CSS 中六个量的概念类似:
  • left:距离 DOM 容器左边界的距离。

  • right:距离 DOM 容器右边界的距离。
  • top:距离 DOM 容器上边界的距离。
  • bottom:距离 DOM 容器下边界的距离。
  • width:宽度。
  • height:高度。 在横向,leftrightwidth 三个量中,只需两个量有值即可,因为任两个量可以决定组件的位置和大小,例如 leftright 或者 rightwidth 都可以决定组件的位置和大小。纵向,topbottomheight 三个量,和横向类同不赘述。

center / radius 定位方式:

  • center

    是一个数组,表示 [x, y],其中,xy可以是『绝对值』或者『百分比』,含义和前述相同。

  • radius

    是一个数组,表示 [内半径, 外半径],其中,内外半径可以是『绝对值』或者『百分比』,含义和前述相同。

    在自适应容器大小时,百分比设置是很有用的。

横向(horizontal)和纵向(vertical)

ECharts的『外观狭长』型的组件(如 legendvisualMapdataZoomtimeline等),大多提供了『横向布局』『纵向布局』的选择。例如,在细长的移动端屏幕上,可能适合使用『纵向布局』;在PC宽屏上,可能适合使用『横向布局』。

横纵向布局的设置,一般在『组件』或者『系列』的 orient 或者 layout 配置项上,设置为 'horizontal' 或者 'vertical'

于 ECharts2 的兼容:

ECharts2 中的 x/x2/y/y2 的命名方式仍被兼容,对应于 left/right/top/bottom。但是建议写 left/right/top/bottom

位置描述中,为兼容 ECharts2,可以支持一些看起来略奇怪的设置:left: 'right'left: 'left'top: 'bottom'top: 'top'。这些语句分别等效于:right: 0left: 0bottom: 0top: 0,写成后者就不奇怪了。

Media Query

Media Query 提供了『随着容器尺寸改变而改变』的能力。

如下例子,可尝试拖动右下角的圆点,随着尺寸变化,legend 和 系列会自动改变布局位置和方式。 https://echarts.baidu.com/gallery/view.html?c=doc-example/pie-media&edit=1&reset=1

要在 option 中设置 Media Query 须遵循如下格式:

  1. option = {
  2. baseOption: { // 这里是基本的『原子option』。
  3. title: {...},
  4. legend: {...},
  5. series: [{...}, {...}, ...],
  6. ...
  7. },
  8. media: [ // 这里定义了 media query 的逐条规则。
  9. {
  10. query: {...}, // 这里写规则。
  11. option: { // 这里写此规则满足下的option。
  12. legend: {...},
  13. ...
  14. }
  15. },
  16. {
  17. query: {...}, // 第二个规则。
  18. option: { // 第二个规则对应的option。
  19. legend: {...},
  20. ...
  21. }
  22. },
  23. { // 这条里没有写规则,表示『默认』,
  24. option: { // 即所有规则都不满足时,采纳这个option。
  25. legend: {...},
  26. ...
  27. }
  28. }
  29. ]
  30. };

上面的例子中,baseOption、以及 media 每个 option 都是『原子 option』,即普通的含有各组件、系列定义的 option。而由『原子option』组合成的整个 option,我们称为『复合 option』。baseOption 是必然被使用的,此外,满足了某个 query 条件时,对应的 option 会被使用 chart.mergeOption() 来 merge 进去。

query:

每个 query 类似于这样:

  1. {
  2. minWidth: 200,
  3. maxHeight: 300,
  4. minAspectRatio: 1.3
  5. }

现在支持三个属性:widthheightaspectRatio(长宽比)。每个属性都可以加上 minmax 前缀。比如,minWidth: 200 表示『大于等于200px宽度』。两个属性一起写表示『并且』,比如:{minWidth: 200, maxHeight: 300} 表示『大于等于200px宽度,并且小于等于300px高度』。

option:

media中的 option 既然是『原子 option』,理论上可以写任何 option 的配置项。但是一般我们只写跟布局定位相关的,例如截取上面例子中的一部分 query option:

  1. media: [
  2. ...,
  3. {
  4. query: {
  5. maxAspectRatio: 1 // 当长宽比小于1时。
  6. },
  7. option: {
  8. legend: { // legend 放在底部中间。
  9. right: 'center',
  10. bottom: 0,
  11. orient: 'horizontal' // legend 横向布局。
  12. },
  13. series: [ // 两个饼图左右布局。
  14. {
  15. radius: [20, '50%'],
  16. center: ['50%', '30%']
  17. },
  18. {
  19. radius: [30, '50%'],
  20. center: ['50%', '70%']
  21. }
  22. ]
  23. }
  24. },
  25. {
  26. query: {
  27. maxWidth: 500 // 当容器宽度小于 500 时。
  28. },
  29. option: {
  30. legend: {
  31. right: 10, // legend 放置在右侧中间。
  32. top: '15%',
  33. orient: 'vertical' // 纵向布局。
  34. },
  35. series: [ // 两个饼图上下布局。
  36. {
  37. radius: [20, '50%'],
  38. center: ['50%', '30%']
  39. },
  40. {
  41. radius: [30, '50%'],
  42. center: ['50%', '75%']
  43. }
  44. ]
  45. }
  46. },
  47. ...
  48. ]

多个 query 被满足时的优先级:

注意,可以有多个 query 同时被满足,会都被 mergeOption,定义在后的后被 merge(即优先级更高)。

默认 query:

如果 media 中有某项不写 query,则表示『默认值』,即所有规则都不满足时,采纳这个option。

容器大小实时变化时的注意事项:

在不少情况下,并不需要容器DOM节点任意随着拖拽变化大小,而是只是根据不同终端设置几个典型尺寸。

但是如果容器DOM节点需要能任意随着拖拽变化大小,那么目前使用时需要注意这件事:某个配置项,如果在某一个 query option 中出现,那么在其他 query option 中也必须出现,否则不能够回归到原来的状态。(left/right/top/bottom/width/height 不受这个限制。)

『复合 option』 中的 media 不支持 merge

也就是说,当第二(或三、四、五 …)次 chart.setOption(rawOption) 时,如果 rawOption复合option(即包含 media 列表),那么新的 rawOption.media 列表不会和老的 media 列表进行 merge,而是简单替代。当然,rawOption.baseOption 仍然会正常和老的 option 进行merge。

其实,很少有场景需要使用『复合 option』来多次 setOption,而我们推荐的做法是,使用 mediaQuery 时,第一次setOption使用『复合 option』,后面 setOption 时仅使用 『原子 option』,也就是仅仅用 setOption 来改变 baseOption

最后看一个和时间轴结合的例子: https://echarts.baidu.com/gallery/view.html?c=doc-example/bar-media-timeline&edit=1&reset=1