提交 3c88f05f 编写于 作者: P pah100

fix #2596 (toolbox restore wrong after setOption at second time using merge mode)

上级 8415b398
......@@ -8,6 +8,7 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
var modelUtil = require('../util/model');
var Model = require('./Model');
var each = zrUtil.each;
var filter = zrUtil.filter;
......@@ -131,11 +132,13 @@ define(function (require) {
function visitComponent(mainType, dependencies) {
var newCptOptionList = newOption[mainType];
if (!(zrUtil.isArray(newCptOptionList))) {
if (!zrUtil.isArray(newCptOptionList)) {
newCptOptionList = newCptOptionList ? [newCptOptionList] : [];
}
var mapResult = mappingToExists(componentsMap[mainType], newCptOptionList);
var mapResult = modelUtil.mappingToExists(
componentsMap[mainType], newCptOptionList
);
makeKeyInfo(mainType, mapResult);
......@@ -580,78 +583,6 @@ define(function (require) {
return ret;
}
/**
* @inner
*/
function mappingToExists(existCpts, newCptOptions) {
// Mapping by the order by original option (but not order of
// new option) in merge mode. Because we should ensure
// some specified index (like xAxisIndex) is consistent with
// original option, which is easy to understand, espatially in
// media query. And in most case, merge option is used to
// update partial option but not be expected to change order.
newCptOptions = (newCptOptions || []).slice();
var result = map(existCpts || [], function (cpt, index) {
return {exist: cpt};
});
// Mapping by id or name if specified.
each(newCptOptions, function (cptOption, index) {
if (!isObject(cptOption)) {
return;
}
for (var i = 0; i < result.length; i++) {
var exist = result[i].exist;
if (!result[i].option // Consider name: two map to one.
&& (
// id has highest priority.
(cptOption.id != null && exist.id === cptOption.id + '')
|| (cptOption.name != null
&& !isIdInner(cptOption)
&& !isIdInner(exist)
&& exist.name === cptOption.name + ''
)
)
) {
result[i].option = cptOption;
newCptOptions[index] = null;
break;
}
}
});
// Otherwise mapping by index.
each(newCptOptions, function (cptOption, index) {
if (!isObject(cptOption)) {
return;
}
var i = 0;
for (; i < result.length; i++) {
var exist = result[i].exist;
if (!result[i].option
&& !isIdInner(exist)
// Caution:
// Do not overwrite id. But name can be overwritten,
// because axis use name as 'show label text'.
// 'exist' always has id and name and we dont
// need to check it.
&& cptOption.id == null
) {
result[i].option = cptOption;
break;
}
}
if (i >= result.length) {
result.push({option: cptOption});
}
});
return result;
}
/**
* @inner
*/
......@@ -769,14 +700,6 @@ define(function (require) {
: components;
}
/**
* @inner
*/
function isIdInner(cptOption) {
// FIXME: Where to put this constant.
return cptOption && cptOption.id && (cptOption.id + '').indexOf('\0_ec_\0') === 0;
}
/**
* @inner
*/
......
......@@ -7,9 +7,12 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
var modelUtil = require('../util/model');
var ComponentModel = require('./Component');
var each = zrUtil.each;
var clone = zrUtil.clone;
var map = zrUtil.map;
var merge = zrUtil.merge;
var QUERY_REG = /^(min|max)?(.+)$/;
......@@ -80,13 +83,13 @@ define(function (require) {
* @private
* @type {Array.<number>}
*/
this._timelineOptions;
this._timelineOptions = [];
/**
* @private
* @type {Array.<Object>}
*/
this._mediaList;
this._mediaList = [];
/**
* @private
......@@ -135,9 +138,27 @@ define(function (require) {
// FIXME
// 如果 timeline options 或者 media 中设置了某个属性,而baseOption中没有设置,则进行警告。
this._optionBackup = parseRawOption.call(
this, rawOption, optionPreprocessorFuncs
);
var oldOptionBackup = this._optionBackup;
var newOptionBackup = parseRawOption.call(this, rawOption, optionPreprocessorFuncs);
// For setOption at second time (using merge mode);
if (oldOptionBackup) {
// Only baseOption can be merged.
newOptionBackup.baseOption = mergeOption(
oldOptionBackup.baseOption, newOptionBackup.baseOption
);
if (!newOptionBackup.timelineOptions.length) {
newOptionBackup.timelineOptions = oldOptionBackup.timelineOptions;
}
if (!newOptionBackup.mediaList.length) {
newOptionBackup.mediaList = oldOptionBackup.mediaList;
}
if (!newOptionBackup.mediaDefault) {
newOptionBackup.mediaDefault = oldOptionBackup.mediaDefault;
}
}
this._optionBackup = newOptionBackup;
},
/**
......@@ -340,5 +361,58 @@ define(function (require) {
return indices1.join(',') === indices2.join(',');
}
/**
* Consider case:
* `chart.setOption(opt1);`
* Then user do some interaction like dataZoom, dataView changing.
* `chart.setOption(opt2);`
* Then user press 'reset button' in toolbox.
*
* After doing that all of the interaction effects should be reset, the
* chart should be the same as the result of invoke
* `chart.setOption(opt1); chart.setOption(opt2);`.
*
* Although it is not able ensure that
* `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to
* `chart.setOption(merge(opt1, opt2));` exactly,
* this might be the only simple way to implement that feature.
*
* MEMO: We've considered some other approaches:
* 1. Each model handle its self restoration but not uniform treatment.
* (Too complex in logic and error-prone)
* 2. Use a shadow ecModel. (Performace expensive)
*/
function mergeOption(oldOption, newOption) {
oldOption = oldOption || {};
newOption = newOption || {};
each(newOption, function (newCptOpt, mainType) {
if (newCptOpt == null) {
return;
}
var oldCptOpt = oldOption[mainType];
if (!ComponentModel.hasClass(mainType)) {
oldOption[mainType] = merge(oldCptOpt, newCptOpt, true);
}
else {
if (!zrUtil.isArray(newCptOpt)) {
newCptOpt = newCptOpt ? [newCptOpt] : [];
}
var mapResult = modelUtil.mappingToExists(oldCptOpt || [], newCptOpt);
oldOption[mainType] = map(mapResult, function (item) {
return (item.option && item.exist)
? merge(item.exist, item.option, true)
: (item.exist || item.option);
});
}
});
return oldOption;
}
return OptionManager;
});
\ No newline at end of file
......@@ -307,5 +307,92 @@ define(function(require) {
}
};
/**
* Mapping to exists for merge.
*
* @public
* @param {Array.<Object>|Array.<module:echarts/model/Component>} exists
* @param {Object|Array.<Object>} newCptOptions
* @return {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
* which order is the same as exists.
*/
modelUtil.mappingToExists = function (exists, newCptOptions) {
// Mapping by the order by original option (but not order of
// new option) in merge mode. Because we should ensure
// some specified index (like xAxisIndex) is consistent with
// original option, which is easy to understand, espatially in
// media query. And in most case, merge option is used to
// update partial option but not be expected to change order.
newCptOptions = (newCptOptions || []).slice();
var result = zrUtil.map(exists || [], function (obj, index) {
return {exist: obj};
});
// Mapping by id or name if specified.
zrUtil.each(newCptOptions, function (cptOption, index) {
if (!zrUtil.isObject(cptOption)) {
return;
}
for (var i = 0; i < result.length; i++) {
var exist = result[i].exist;
if (!result[i].option // Consider name: two map to one.
&& (
// id has highest priority.
(cptOption.id != null && exist.id === cptOption.id + '')
|| (cptOption.name != null
&& !isIdInner(cptOption)
&& !isIdInner(exist)
&& exist.name === cptOption.name + ''
)
)
) {
result[i].option = cptOption;
newCptOptions[index] = null;
break;
}
}
});
// Otherwise mapping by index.
zrUtil.each(newCptOptions, function (cptOption, index) {
if (!zrUtil.isObject(cptOption)) {
return;
}
var i = 0;
for (; i < result.length; i++) {
var exist = result[i].exist;
if (!result[i].option
&& !isIdInner(exist)
// Caution:
// Do not overwrite id. But name can be overwritten,
// because axis use name as 'show label text'.
// 'exist' always has id and name and we dont
// need to check it.
&& cptOption.id == null
) {
result[i].option = cptOption;
break;
}
}
if (i >= result.length) {
result.push({option: cptOption});
}
});
return result;
};
/**
* @inner
*/
function isIdInner(cptOption) {
// FIXME: Where to put this constant.
return cptOption && cptOption.id && (cptOption.id + '').indexOf('\0_ec_\0') === 0;
}
return modelUtil;
});
\ No newline at end of file
......@@ -23,13 +23,58 @@
height: 650px;
background: #fff;
}
.code-panel {
position: fixed;
top: 10px;
right: 10px;
width: 200px;
border: 2px solid #555;
}
.code-line {
margin: 15px 5px;
font-size: 12px;
}
.code-line textarea{
width: 190px;
height: 80px;
margin-bottom: 5px;
}
</style>
<div id="main"></div>
<div class="code-panel">
<div class="code-line">
<textarea id="code1">
chart.setOption({backgroundColor: '#000'});
</textarea>
<input type="button" value="run" onclick="runCode('code1');"/>
Then press restore buttton.
</div>
<div class="code-line">
<textarea id="code2">
chart.setOption({backgroundColor: '#fff'});
</textarea>
<input type="button" value="run" onclick="runCode('code2');"/>
</div>
<div class="code-line">
<textarea id="code3">
chart.setOption({
series: [
{id: 'pie0', label: {normal: {position: 'inside'}}}
]
});
</textarea>
<input type="button" value="run" onclick="runCode('code3');"/>
</div>
</div>
<script src="data/timelineGDP.js"></script>
<script>
var chart;
var echarts;
// markLine: {
// symbol: ['arrow','none'],
// symbolSize: [4, 2],
......@@ -57,8 +102,10 @@
'echarts/component/legend',
'echarts/component/grid',
'echarts/component/tooltip',
'echarts/component/timeline'
], function (echarts) {
'echarts/component/timeline',
'echarts/component/toolbox'
], function (ec) {
echarts = ec;
chart = echarts.init(document.getElementById('main'), null, {
renderer: 'canvas'
......@@ -119,6 +166,13 @@ for (var i = 0; i < categoryData.length; i++) {
}
}
},
toolbox: {
left: 0,
bottom: 0,
feature: {
restore: {}
}
},
title: {
subtext: 'Media Query 示例'
},
......@@ -154,7 +208,7 @@ for (var i = 0; i < categoryData.length; i++) {
{name: '第一产业', type: 'bar'},
{name: '第二产业', type: 'bar'},
{name: '第三产业', type: 'bar'},
{name: 'GDP占比', type: 'pie'}
{name: 'GDP占比', type: 'pie', id: 'pie0'}
]
},
media: [
......@@ -312,7 +366,7 @@ for (var i = 0; i < categoryData.length; i++) {
{name: '第一产业', value: dataMap.dataPI['2002sum']},
{name: '第二产业', value: dataMap.dataSI['2002sum']},
{name: '第三产业', value: dataMap.dataTI['2002sum']}
]}
], id: 'pie0'}
]
},
{
......@@ -328,7 +382,7 @@ for (var i = 0; i < categoryData.length; i++) {
{name: '第一产业', value: dataMap.dataPI['2003sum']},
{name: '第二产业', value: dataMap.dataSI['2003sum']},
{name: '第三产业', value: dataMap.dataTI['2003sum']}
]}
], id: 'pie0'}
]
},
{
......@@ -344,7 +398,7 @@ for (var i = 0; i < categoryData.length; i++) {
{name: '第一产业', value: dataMap.dataPI['2004sum']},
{name: '第二产业', value: dataMap.dataSI['2004sum']},
{name: '第三产业', value: dataMap.dataTI['2004sum']}
]}
], id: 'pie0'}
]
},
{
......@@ -360,7 +414,7 @@ for (var i = 0; i < categoryData.length; i++) {
{name: '第一产业', value: dataMap.dataPI['2005sum']},
{name: '第二产业', value: dataMap.dataSI['2005sum']},
{name: '第三产业', value: dataMap.dataTI['2005sum']}
]}
], id: 'pie0'}
]
},
{
......@@ -376,7 +430,7 @@ for (var i = 0; i < categoryData.length; i++) {
{name: '第一产业', value: dataMap.dataPI['2006sum']},
{name: '第二产业', value: dataMap.dataSI['2006sum']},
{name: '第三产业', value: dataMap.dataTI['2006sum']}
]}
], id: 'pie0'}
]
},
{
......@@ -392,7 +446,7 @@ for (var i = 0; i < categoryData.length; i++) {
{name: '第一产业', value: dataMap.dataPI['2007sum']},
{name: '第二产业', value: dataMap.dataSI['2007sum']},
{name: '第三产业', value: dataMap.dataTI['2007sum']}
]}
], id: 'pie0'}
]
},
{
......@@ -408,7 +462,7 @@ for (var i = 0; i < categoryData.length; i++) {
{name: '第一产业', value: dataMap.dataPI['2008sum']},
{name: '第二产业', value: dataMap.dataSI['2008sum']},
{name: '第三产业', value: dataMap.dataTI['2008sum']}
]}
], id: 'pie0'}
]
},
{
......@@ -424,7 +478,7 @@ for (var i = 0; i < categoryData.length; i++) {
{name: '第一产业', value: dataMap.dataPI['2009sum']},
{name: '第二产业', value: dataMap.dataSI['2009sum']},
{name: '第三产业', value: dataMap.dataTI['2009sum']}
]}
], id: 'pie0'}
]
},
{
......@@ -440,7 +494,7 @@ for (var i = 0; i < categoryData.length; i++) {
{name: '第一产业', value: dataMap.dataPI['2010sum']},
{name: '第二产业', value: dataMap.dataSI['2010sum']},
{name: '第三产业', value: dataMap.dataTI['2010sum']}
]}
], id: 'pie0'}
]
},
{
......@@ -456,16 +510,12 @@ for (var i = 0; i < categoryData.length; i++) {
{name: '第一产业', value: dataMap.dataPI['2011sum']},
{name: '第二产业', value: dataMap.dataSI['2011sum']},
{name: '第三产业', value: dataMap.dataTI['2011sum']}
]}
], id: 'pie0'}
]
}
]
};
chart.setOption(option);
chart.on('legendSelected', function () {
......@@ -474,5 +524,15 @@ for (var i = 0; i < categoryData.length; i++) {
window.onresize = chart.resize;
});
</script>
<script type="text/javascript">
function runCode(id) {
var textarea = document.getElementById(id);
var code = textarea.value;
(new Function('chart', 'echarts', code))(chart, echarts);
}
</script>
</body>
</html>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册