This is an automated email from the ASF dual-hosted git repository. sushuang pushed a commit to branch fix/label-valueAnimation in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit ab9aa43e7b0f782c9c6febc12c9c7ecc11847d11 Author: 100pah <[email protected]> AuthorDate: Thu Dec 10 21:43:08 2020 +0800 fix: when label animation is not finished, start a new label animation, the label should not jump the the last final value. --- src/label/labelStyle.ts | 17 +- test/label-animation.html | 329 ++++++++++++++++++++---------- test/lib/testHelper.js | 25 ++- test/line-endLabel.html | 87 +++++++- test/runTest/actions/__meta__.json | 2 + test/runTest/actions/label-animation.json | 1 + test/runTest/actions/line-endLabel.json | 1 + 7 files changed, 344 insertions(+), 118 deletions(-) diff --git a/src/label/labelStyle.ts b/src/label/labelStyle.ts index 7934fe5..2a8caed 100644 --- a/src/label/labelStyle.ts +++ b/src/label/labelStyle.ts @@ -620,10 +620,14 @@ export const labelInner = makeInner<{ */ prevValue?: ParsedValue | ParsedValue[] /** - * Current value stored used for label. + * Target value stored used for label. */ value?: ParsedValue | ParsedValue[] /** + * Current value in text animation. + */ + interpolatedValue?: ParsedValue | ParsedValue[] + /** * If enable value animation */ valueAnimation?: boolean @@ -657,7 +661,9 @@ export function setLabelValueAnimation( } const obj = labelInner(label); - obj.prevValue = obj.value; + // Consider the case that being animating, do not use the `obj.value`, + // Otherwise it will jump to the `obj.value` when this new animation started. + obj.prevValue = retrieve2(obj.interpolatedValue, obj.value); obj.value = value; const normalLabelModel = labelStatesModels.normal; @@ -693,6 +699,7 @@ export function animateLabelValue( currentValue, percent ); + labelInnerStore.interpolatedValue = percent === 1 ? null : interpolated; const labelText = getLabelText({ labelDataIndex: dataIndex, @@ -705,6 +712,8 @@ export function animateLabelValue( setLabelText(textEl, labelText); } - (prevValue == null ? initProps - : updateProps)(textEl, {}, seriesModel, dataIndex, null, during); + (prevValue == null + ? initProps + : updateProps + )(textEl, {}, seriesModel, dataIndex, null, during); } \ No newline at end of file diff --git a/test/label-animation.html b/test/label-animation.html index e9940e9..5a534d8 100644 --- a/test/label-animation.html +++ b/test/label-animation.html @@ -39,11 +39,13 @@ under the License. <div id="main0"></div> <div id="main1"></div> + <div id="main2"></div> + <script> - function makeSource1() { + function makeSource0() { return [ [ 'Int','Country', 'Float', 'Num'], [900, 'Germany', 90.55, 0], @@ -56,6 +58,17 @@ under the License. [1200, 'France', 120.55, 3], ]; } + function makeSource1() { + return { + category: ['Germany', 'France'], + dataList: [ + [900, 1900], + [300, 1300], + [800, 1800], + [200, 1200] + ] + }; + } </script> @@ -67,6 +80,42 @@ under the License. var option; var currNum = 0; + var formatterConfigs = [ + { + text: 'formatter: {c} 元', + formatter: '{c} 元' + }, + { + text: 'formatter: {@[1]} {@[0]} 元', + formatter: '{@[1]} {@[0]} 元' + }, + { + text: 'formatter: {@Country} {@Int} 元', + formatter: '{@Country} {@Int} 元' + }, + { + text: 'formatter: callback', + formatter: function (params) { + return params.value.join(',') + ' 元'; + } + } + ]; + var formatterSwitchButtons = []; + for (var i = 0; i < formatterConfigs.length; i++) { + var config = formatterConfigs[i]; + formatterSwitchButtons.push({ + text: config.text, + onclick: (function (cfg) { + return function () { + currNum = 0; + var option = makeOption(cfg.formatter); + chart.__testHelper.updateInfo(cfg.formatter.toString(), 'formatter'); + chart.setOption(option, { notMerge: true } ); + }; + })(config) + }); + } + function makeTransformDataset() { return { id: 'singleA', @@ -88,68 +137,57 @@ under the License. }; } - option = { - animationDuration: 5000, - animationDurationUpdate: 5000, - dataset: [{ - source: makeSource1(), - }, - makeTransformDataset() - ], - xAxis: {}, - yAxis: { type: 'category' }, - grid: { - right: 160 - }, - series: [{ - type: 'bar', - datasetId: 'singleA', - encode: { - x: 'Int', - y: 'Country' + function makeOption(labelFormatter) { + return { + animationDuration: 3000, + animationDurationUpdate: 3000, + dataset: [{ + source: makeSource0(), }, - label: { - show: true, - position: 'right', - fontSize: 16, - formatter: '(StrFmt) {c} 元', - valueAnimation: true - } - }, { - type: 'bar', - datasetId: 'singleA', - encode: { - x: 'Int', - y: 'Country' + makeTransformDataset() + ], + xAxis: {}, + yAxis: { type: 'category' }, + grid: { + right: 160 }, - label: { - show: true, - position: 'right', - fontSize: 16, - formatter: function (params) { - console.log(params); - return '(CbFmt) ' + params.value.join(',') + ' 元'; + series: [{ + id: Math.random(), + type: 'bar', + datasetId: 'singleA', + encode: { + x: 'Int', + y: 'Country' }, - valueAnimation: true - } - }] - }; + label: { + show: true, + position: 'right', + fontSize: 16, + formatter: labelFormatter, + valueAnimation: true + } + }] + }; + } var chart = testHelper.create(echarts, 'main0', { title: [ - 'Check init and **click next once**', - 'label text anmiation should OK. **except Country**', - 'label should display like : "910,France,617.52,0 元"', + 'Check **init** valueAnimation should be OK', + 'Swithch formatter, for each formatter **click next once**', + 'label text anmiation should OK. **except Country**' ], - option: option, + option: makeOption(formatterConfigs[0].formatter), + info: formatterConfigs[0].formatter.toString(), + infoKey: 'formatter', buttons: [{ - text: 'next', onclick: function () { + text: 'next', + onclick: function () { currNum++; chart.setOption({ dataset: makeTransformDataset() }); } - }], + }].concat(formatterSwitchButtons) }); }); </script> @@ -162,92 +200,171 @@ under the License. + + + + + + <script> require(['echarts'/*, 'map/js/china' */], function (echarts) { var option; var currNum = 0; - function makeTransformDataset() { + var formatterConfigs = [ + { + text: 'formatter: {c} 元', + formatter: '{c} 元' + }, + // { // not supportted in this case + // text: 'formatter: {@[1]} {@[0]} 元', + // formatter: '{@[1]} {@[0]} 元' + // }, + { + text: 'formatter: callback', + formatter: function (params) { + return params.value + ' 元'; + } + } + ]; + var formatterSwitchButtons = []; + for (var i = 0; i < formatterConfigs.length; i++) { + var config = formatterConfigs[i]; + formatterSwitchButtons.push({ + text: config.text, + onclick: (function (cfg) { + return function () { + currNum = 0; + var option = makeOption(cfg.formatter); + chart.__testHelper.updateInfo(cfg.formatter.toString(), 'formatter'); + chart.setOption(option, { notMerge: true } ); + }; + })(config) + }); + } + + var source1 = makeSource1(); + + function makeOption(labelFormatter) { return { - id: 'singleA', - transform: { - type: 'filter', - print: true, - config: { - and: [{ - or: [{ - dimension: 'Country', eq: 'Germany' - }, { - dimension: 'Country', eq: 'France' - }] - }, { - dimension: 'Num', eq: currNum - }] + animationDuration: 3000, + animationDurationUpdate: 3000, + xAxis: {}, + yAxis: { + type: 'category', + data: source1.category + }, + grid: { + right: 160 + }, + series: [{ + id: Math.random(), + type: 'bar', + data: source1.dataList[currNum], + label: { + show: true, + position: 'right', + fontSize: 16, + formatter: labelFormatter, + valueAnimation: true } - } + }] }; } - option = { + var chart = testHelper.create(echarts, 'main1', { + title: [ + 'Check **init** valueAnimation should be OK', + 'Swithch formatter, for each formatter **click next once**', + 'label text anmiation should OK. **except Country**' + ], + option: makeOption(formatterConfigs[0].formatter), + info: formatterConfigs[0].formatter.toString(), + infoKey: 'formatter', + buttons: [{ + text: 'next', + onclick: function () { + currNum++; + chart.setOption({ + series: { + data: source1.dataList[currNum] + } + }); + } + }].concat(formatterSwitchButtons) + }); + }); + </script> + + + + + + + + + + <script> + require(['echarts'/*, 'map/js/china' */], function (echarts) { + var valueA = 100; + var valueB = 200; + + var option = { animationDuration: 5000, animationDurationUpdate: 5000, - dataset: [{ - source: makeSource1(), - }, - makeTransformDataset() - ], + animationEasing: 'linear', + animationEasingUpdate: 'linear', xAxis: {}, - yAxis: { type: 'category' }, + yAxis: { + type: 'category', + data: ['A', 'B'] + }, grid: { right: 160 }, series: [{ type: 'bar', - datasetId: 'singleA', - encode: { - x: 'Int', - y: 'Country' - }, - label: { - show: true, - position: 'right', - fontSize: 16, - formatter: '(StrFmt1) {@Country} {@Int} 元', - valueAnimation: true - } - }, { - type: 'bar', - datasetId: 'singleA', - encode: { - x: 'Int', - y: 'Country' - }, + data: [valueA, valueB], label: { show: true, position: 'right', fontSize: 16, - formatter: '(StrFmt2) {@[1]} {@[0]} 元', + formatter: '{c} ton', valueAnimation: true } }] }; - var chart = testHelper.create(echarts, 'main1', { + function updateChart() { + chart.setOption({ + series: [{ + data: [valueA, valueB] + }] + }); + } + + var chart = testHelper.create(echarts, 'main2', { title: [ - 'Check init and **click next once**', - 'label text anmiation should OK. **except Country**', - 'label should display like : "France 910 元"', + 'Check A++ **before value animation finished**.', + 'B should keep on label animation, rather than jump to the final label.', + 'B should finally reach at 200. A should finally reach at 600' ], option: option, buttons: [{ - text: 'next', onclick: function () { - currNum++; - chart.setOption({ - dataset: makeTransformDataset() - }); + text: 'A++', + onclick: function () { + valueA += 500; + updateChart(); + } + }, { + text: 'B++', + onclick: function () { + valueB += 500; + updateChart(); } - }], + }] }); + }); </script> @@ -256,12 +373,6 @@ under the License. - - - - - - </body> </html> diff --git a/test/lib/testHelper.js b/test/lib/testHelper.js index 88dc879..be1d491 100644 --- a/test/lib/testHelper.js +++ b/test/lib/testHelper.js @@ -144,11 +144,19 @@ } if (opt.info) { - infoContainer.innerHTML = createObjectHTML(opt.info, opt.infoKey || 'option'); + updateInfo(opt.info, opt.infoKey); + } + + function updateInfo(info, infoKey) { + infoContainer.innerHTML = createObjectHTML(info, infoKey || 'option'); } initRecordCanvas(opt, chart, recordCanvasContainer); + chart.__testHelper = { + updateInfo: updateInfo + }; + return chart; }; @@ -932,9 +940,15 @@ } function createObjectHTML(obj, key) { + var html = isObject(obj) + ? testHelper.encodeHTML(printObject(obj, key)) + : obj + ? obj.toString() + : ''; + return [ '<pre class="test-print-object">', - testHelper.encodeHTML(printObject(obj, key)), + html, '</pre>' ].join(''); } @@ -977,6 +991,13 @@ return params ? params.split('&') : []; } + function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + const type = typeof value; + return type === 'function' || (!!value && type === 'object'); + } + context.testHelper = testHelper; })(window); \ No newline at end of file diff --git a/test/line-endLabel.html b/test/line-endLabel.html index b5826cb..3fcbd04 100644 --- a/test/line-endLabel.html +++ b/test/line-endLabel.html @@ -36,7 +36,90 @@ under the License. height: 1000px; margin-bottom: 30px; } + #formatterSwitchButtons { + margin: 10px; + padding: 10px; + } + #currentFormatter { + margin: 10px; + padding: 10px; + border: 1px solid #aaa; + } </style> + + <div id="formatterSwitchButtons"> + </div> + <div> + Current formatter: + <pre id="currentFormatter"></pre> + </div> + + <script> + var _endLabelFormatter; + + // Init formatter switch buttons. + (function () { + + var formatterConfigs = [ + { + text: 'formatter string {c}', + formatter: '$ {c}' + }, + { + text: 'formatter string {@[dimIndex]}', + formatter: '$ {@[1]}' + }, + { + text: 'formatter callback', + formatter: function (param) { + return '$ ' + param.value; + } + } + ]; + var END_LABEL_FORMATTER = '__EC_TEST_line-endLabel.html_FOMRATTER___'; + + function setLocalStorage(formatterKey) { + window.localStorage.setItem(END_LABEL_FORMATTER, formatterKey); + } + function getLocalStorage() { + return window.localStorage.getItem(END_LABEL_FORMATTER); + } + + var currFormatterKey = getLocalStorage(); + var fmtBtnBox = document.getElementById('formatterSwitchButtons'); + for (var i = 0; i < formatterConfigs.length; i++) { + var config = formatterConfigs[i]; + + if (_endLabelFormatter == null + && ( + currFormatterKey == null + || currFormatterKey === config.text + ) + ) { + _endLabelFormatter = config.formatter; + } + + var btn = document.createElement('button'); + btn.innerHTML = config.text; + btn.onclick = (function (cfg) { + return function () { + setLocalStorage(cfg.text); + location.reload(); + }; + })(config); + fmtBtnBox.appendChild(btn); + } + + if (!_endLabelFormatter) { + throw new Error(); + } + var formatterDisplayBox = document.getElementById('currentFormatter'); + formatterDisplayBox.innerHTML = _endLabelFormatter.toString(); + })(); + + </script> + + <div id="main0"></div> <div id="main1"></div> <script> @@ -200,9 +283,7 @@ under the License. }, endLabel: { show: true, - formatter: function (param) { - return '$' + param.value; - }, + formatter: _endLabelFormatter, fontSize: 14 }, emphasis: { diff --git a/test/runTest/actions/__meta__.json b/test/runTest/actions/__meta__.json index 663ac84..f6eb460 100644 --- a/test/runTest/actions/__meta__.json +++ b/test/runTest/actions/__meta__.json @@ -91,6 +91,7 @@ "hoverFocus2": 2, "hoverStyle": 14, "hoverStyle2": 1, + "label-animation": 3, "label-layout": 1, "label-position": 1, "largeLine-tooltip": 1, @@ -100,6 +101,7 @@ "line-animation": 1, "line-boldWhenHover": 1, "line-crash": 1, + "line-endLabel": 1, "map": 3, "map-contour": 2, "map-default": 1, diff --git a/test/runTest/actions/label-animation.json b/test/runTest/actions/label-animation.json new file mode 100644 index 0000000..045bf8a --- /dev/null +++ b/test/runTest/actions/label-animation.json @@ -0,0 +1 @@ +[{"name":"Action 1","ops":[{"type":"mousemove","time":476,"x":107,"y":131},{"type":"mousemove","time":676,"x":107,"y":118},{"type":"mousedown","time":820,"x":108,"y":115},{"type":"mousemove","time":885,"x":108,"y":115},{"type":"mouseup","time":952,"x":108,"y":115},{"time":953,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":3025,"x":105,"y":115},{"type":"mousemove","time":3225,"x":31,"y":128},{"type":"mousemove","time":3425,"x":20,"y":126},{"type":"mousemove","time":3633, [...] \ No newline at end of file diff --git a/test/runTest/actions/line-endLabel.json b/test/runTest/actions/line-endLabel.json new file mode 100644 index 0000000..712dc3e --- /dev/null +++ b/test/runTest/actions/line-endLabel.json @@ -0,0 +1 @@ +[{"name":"Action 1","ops":[{"type":"mousemove","time":732,"x":86,"y":167},{"type":"mousemove","time":932,"x":85,"y":126},{"type":"mousemove","time":1132,"x":85,"y":63},{"type":"mousemove","time":1339,"x":94,"y":30},{"type":"mousemove","time":1556,"x":95,"y":28},{"type":"mousedown","time":1764,"x":95,"y":28},{"type":"mouseup","time":1928,"x":95,"y":28},{"time":1929,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":4451,"x":96,"y":28},{"type":"mousemove","time":4667,"x":135, [...] \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
