ECharts实现移动端自适应
用户使用 Echarts 工作的时候所需要用到的 组件 和 系列 都在指定高宽的 DOM 节点(容器)中,其中每个节点都可以由用户指定位置。
Echarts 图表库内部采用的是类似于绝对布局的易于理解的布局方式,因为实现 DOM 文档流布局是不合适的。但是图表库所采用的布局方式会受到容器尺寸的影响,出现组件重叠的情况。这就带来了一个问题:如果图表需要同时在 PC端和移动端展示,该怎么解决内部组件的布局?
上述问题需要 Echarts 内部组件能够自动地随着容器尺寸的改变而进行调整的能力。为了解决这个问题,ECharts 完善了组件的定位设置,并且实现了类似 CSS Media Query 的自适应能力。
ECharts 组件的定位和布局
Echarts 中大部分组件和系列会遵循两种定位方式:
- left/right/top/bottom/width/height 定位方式
- center / radius 定位方式
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:表示高度。
小提示:
在表示横向的量的时候,由于组件的位置和大小可以由任意的两个量决定,所以在 left、right、width 三个量中,可以只提供两个量的值。至于取哪两个量的值就取决于用户,例如 left 和 right 或者 right 和 width 都可以决定组件的位置和大小。
同理,在表示纵向的量 top、bottom、height 时,取值与横向量相同。
center / radius 定位方式
- center 是一个数组,表示 [x, y],其中,x、y 可以是绝对值或者百分比,含义与前面描述的相同。
- radius 是一个数组,表示 [内半径, 外半径],其中,内外半径可以是绝对值或者百分比,含义与前面描述的相同。
注意:在自适应容器大小时,百分比设置是很有用的。
横向(horizontal)和纵向(vertical)
ECharts 中像 legend、visualMap、dataZoom、timeline等狭长型的组件,大部分都有横向布局和纵向布局这两种选择。例如,在宽少长多的移动端屏幕上,使用纵向布局更为合适;而在屏幕较宽的 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: 0、left: 0、bottom: 0、top: 0,写成后者就不奇怪了。
Media Query
Media Query 提供了随着容器尺寸改变而改变的能力。
下面的例子,可以拖动右下角的圆点改变尺寸,随着尺寸变化,legend 和 系列会自动改变布局位置和方式:
在 option 中设置 Media Query 需要遵循下面的格式:
option = {
baseOption: { // 这里是基本的『原子option』。
title: {...},
legend: {...},
series: [{...}, {...}, ...],
...
},
media: [ // 这里定义了 media query 的逐条规则。
{
query: {...}, // 这里写规则。
option: { // 这里写此规则满足下的option。
legend: {...},
...
}
},
{
query: {...}, // 第二个规则。
option: { // 第二个规则对应的option。
legend: {...},
...
}
},
{ // 这条里没有写规则,表示『默认』,
option: { // 即所有规则都不满足时,采纳这个option。
legend: {...},
...
}
}
]
};
上述例子中的 baseOption、以及 media 每个 option 都是原子 option,即普通的含有各组件、系列定义的 option。而由原子option组合成的整个 option,我们称为复合 option。baseOption 是必然被使用的,此外,满足了某个 query 条件时,对应的 option 会被使用 chart.mergeOption() 来 merge 进去。
query:
每个 query 可以写成下述形式:
{
minWidth: 200,
maxHeight: 300,
minAspectRatio: 1.3
}
目前 query 支持三个属性:width、height、aspectRatio(长宽比)。每个属性都可以加上 min 或 max 前缀。比如,minWidth: 200 表示 大于等于200px宽度 。两个属性一起写表示 且 ,例如:{minWidth: 200, maxHeight: 300} 表示 大于等于200px宽度,且小于等于300px高度 。
option:
media 中的 option 既然是 原子 option ,理论上可以写任何 option 的配置项。但是一般我们只写跟布局定位相关的,例如截取上面例子中的一部分 query option:
media: [
...,
{
query: {
maxAspectRatio: 1 // 当长宽比小于1时。
},
option: {
legend: { // legend 放在底部中间。
right: 'center',
bottom: 0,
orient: 'horizontal' // legend 横向布局。
},
series: [ // 两个饼图左右布局。
{
radius: [20, '50%'],
center: ['50%', '30%']
},
{
radius: [30, '50%'],
center: ['50%', '70%']
}
]
}
},
{
query: {
maxWidth: 500 // 当容器宽度小于 500 时。
},
option: {
legend: {
right: 10, // legend 放置在右侧中间。
top: '15%',
orient: 'vertical' // 纵向布局。
},
series: [ // 两个饼图上下布局。
{
radius: [20, '50%'],
center: ['50%', '30%']
},
{
radius: [30, '50%'],
center: ['50%', '75%']
}
]
}
},
...
]
具有多个 query 时的优先级:
注意,可以有多个 query 同时被满足,会都被 mergeOption,定义在后的后被 merge(即优先级更高)。
默认 query:
如果 media 中有某项不写 query,则表示『默认值』,即所有规则都不满足时,采纳这个option。
容器大小实时变化时的注意事项:
如果容器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。
最后看一个和时间轴结合的例子: