This is an automated email from the ASF dual-hosted git repository. sushuang pushed a commit to branch PR/plainheart_fix/alignTicks-precision in repository https://gitbox.apache.org/repos/asf/echarts.git
commit eb7530b3ed0687dcdc32eb68a9f2751386eb8333 Author: 100pah <[email protected]> AuthorDate: Sun Mar 8 17:34:52 2026 +0800 fix(appendData): Fix that the dataZoom inside is disabled when appendData is executed. And clarify the usage of appendData in comments. --- src/component/dataZoom/roams.ts | 119 ++++++++++++++++++++-------------------- src/core/echarts.ts | 31 ++++++----- src/core/lifecycle.ts | 1 + src/util/model.ts | 7 ++- src/util/types.ts | 15 ++--- test/candlestick-large3.html | 21 ++++--- 6 files changed, 101 insertions(+), 93 deletions(-) diff --git a/src/component/dataZoom/roams.ts b/src/component/dataZoom/roams.ts index 38800b8c3..acd1b30fe 100644 --- a/src/component/dataZoom/roams.ts +++ b/src/component/dataZoom/roams.ts @@ -18,7 +18,7 @@ */ // Only create one roam controller for each coordinate system. -// one roam controller might be refered by two inside data zoom +// one roam controller might be referred by two inside data zoom // components (for example, one for x and one for y). When user // pan or zoom, only dispatch one action for those data zoom // components. @@ -39,7 +39,6 @@ import { CoordinateSystemHostModel } from '../../coord/CoordinateSystem'; import { DataZoomGetRangeHandlers } from './InsideZoomView'; import { EChartsExtensionInstallRegisters } from '../../extension'; - interface DataZoomInfo { getRange: DataZoomGetRangeHandlers; model: InsideZoomModel; @@ -240,71 +239,69 @@ function mergeControllerParams( export function installDataZoomRoamProcessor(registers: EChartsExtensionInstallRegisters) { - registers.registerProcessor( - registers.PRIORITY.PROCESSOR.FILTER, - function (ecModel: GlobalModel, api: ExtensionAPI): void { - const apiInner = inner(api); - const coordSysRecordMap = apiInner.coordSysRecordMap - || (apiInner.coordSysRecordMap = createHashMap<CoordSysRecord, string>()); - - coordSysRecordMap.each(function (coordSysRecord) { - // `coordSysRecordMap` always exists (because it holds the `roam controller`, which should - // better not re-create each time), but clear `dataZoomInfoMap` each round of the workflow. - coordSysRecord.dataZoomInfoMap = null; - }); + registers.registerUpdateLifecycle('coordsys:aftercreate', (ecModel, api) => { + const apiInner = inner(api); + const coordSysRecordMap = apiInner.coordSysRecordMap + || (apiInner.coordSysRecordMap = createHashMap<CoordSysRecord, string>()); - ecModel.eachComponent( - { mainType: 'dataZoom', subType: 'inside' }, - function (dataZoomModel: InsideZoomModel) { - const dzReferCoordSysWrap = collectReferCoordSysModelInfo(dataZoomModel); - - each(dzReferCoordSysWrap.infoList, function (dzCoordSysInfo) { - - const coordSysUid = dzCoordSysInfo.model.uid; - const coordSysRecord = coordSysRecordMap.get(coordSysUid) - || coordSysRecordMap.set(coordSysUid, createCoordSysRecord(api, dzCoordSysInfo.model)); - - const dataZoomInfoMap = coordSysRecord.dataZoomInfoMap - || (coordSysRecord.dataZoomInfoMap = createHashMap<DataZoomInfo, string>()); - // Notice these props might be changed each time for a single dataZoomModel. - dataZoomInfoMap.set(dataZoomModel.uid, { - dzReferCoordSysInfo: dzCoordSysInfo, - model: dataZoomModel, - getRange: null - }); - }); - } - ); + coordSysRecordMap.each(function (coordSysRecord) { + // `coordSysRecordMap` always exists (because it holds the `roam controller`, which should + // better not re-create each time), but clear `dataZoomInfoMap` each round of the workflow. + coordSysRecord.dataZoomInfoMap = null; + }); - // (1) Merge dataZoom settings for each coord sys and set to the roam controller. - // (2) Clear coord sys if not refered by any dataZoom. - coordSysRecordMap.each(function (coordSysRecord) { - const controller = coordSysRecord.controller; - let firstDzInfo: DataZoomInfo; - const dataZoomInfoMap = coordSysRecord.dataZoomInfoMap; - - if (dataZoomInfoMap) { - const firstDzKey = dataZoomInfoMap.keys()[0]; - if (firstDzKey != null) { - firstDzInfo = dataZoomInfoMap.get(firstDzKey); - } - } + ecModel.eachComponent( + { mainType: 'dataZoom', subType: 'inside' }, + function (dataZoomModel: InsideZoomModel) { + const dzReferCoordSysWrap = collectReferCoordSysModelInfo(dataZoomModel); - if (!firstDzInfo) { - disposeCoordSysRecord(coordSysRecordMap, coordSysRecord); - return; + each(dzReferCoordSysWrap.infoList, function (dzCoordSysInfo) { + + const coordSysUid = dzCoordSysInfo.model.uid; + const coordSysRecord = coordSysRecordMap.get(coordSysUid) + || coordSysRecordMap.set(coordSysUid, createCoordSysRecord(api, dzCoordSysInfo.model)); + + const dataZoomInfoMap = coordSysRecord.dataZoomInfoMap + || (coordSysRecord.dataZoomInfoMap = createHashMap<DataZoomInfo, string>()); + // Notice these props might be changed each time for a single dataZoomModel. + dataZoomInfoMap.set(dataZoomModel.uid, { + dzReferCoordSysInfo: dzCoordSysInfo, + model: dataZoomModel, + getRange: null + }); + }); + } + ); + + // (1) Merge dataZoom settings for each coord sys and set to the roam controller. + // (2) Clear coord sys if not referred by any dataZoom. + coordSysRecordMap.each(function (coordSysRecord) { + const controller = coordSysRecord.controller; + let firstDzInfo: DataZoomInfo; + const dataZoomInfoMap = coordSysRecord.dataZoomInfoMap; + + if (dataZoomInfoMap) { + const firstDzKey = dataZoomInfoMap.keys()[0]; + if (firstDzKey != null) { + firstDzInfo = dataZoomInfoMap.get(firstDzKey); } + } + + if (!firstDzInfo) { + disposeCoordSysRecord(coordSysRecordMap, coordSysRecord); + return; + } - const controllerParams = mergeControllerParams(dataZoomInfoMap, coordSysRecord, api); - controller.enable(controllerParams.controlType, controllerParams.opt); + const controllerParams = mergeControllerParams(dataZoomInfoMap, coordSysRecord, api); + controller.enable(controllerParams.controlType, controllerParams.opt); - throttleUtil.createOrUpdate( - coordSysRecord, - 'dispatchAction', - firstDzInfo.model.get('throttle', true), - 'fixRate' - ); - }); + throttleUtil.createOrUpdate( + coordSysRecord, + 'dispatchAction', + firstDzInfo.model.get('throttle', true), + 'fixRate' + ); + }); }); } diff --git a/src/core/echarts.ts b/src/core/echarts.ts index ad5bb2bdc..a68bddad0 100644 --- a/src/core/echarts.ts +++ b/src/core/echarts.ts @@ -233,30 +233,31 @@ export const PRIORITY = { * * - EC_PROGRESSIVE_CYCLE: * - It also carries out a series of processing/updating/rendering, but out of EC_MAIN_CYCLE. - * - It is performed in each "animation frame". - * - It can be triggered internally or `appendData` call. + * - It is performed in each subsequent "animation frame" until finished. + * - It can be triggered by EC_MAIN_CYCLE or EC_APPEND_DATA_CYCLE. * - A run of EC_PROGRESSIVE_CYCLE comprises: * - Data processing (may be absent) (see `registerProcessor`) * - Visual encoding (may be absent) (see `registerVisual`) * - Layout (may be absent) (see `registerLayout`) * - Rendering (`ComponentView` or `SeriesView`) * - PENDING: currently all data processing tasks (via `registerProcessor`) run in "block" mode. - * (see `performDataProcessorTasks`) + * (see `performDataProcessorTasks`). * * - Other updating/rendering cycles: + * - EC_APPEND_DATA_CYCLE (see `appendData`) is only supported for some special cases. * - Some series have specific update/render cycles. For example, graph force layout performs - * layout and rendering in each "animation frame". + * layout and rendering in each "animation frame". * * - Model updating: * - Model can only be modified at the beginning of ec cycles, including only: * - EC_PREPARE_UPDATE (see method `prepare()`) in `setOption` call. * - EC action handlers in `dispatchAction` call. - * - `appendData` (a special case, where only data is modified). + * - `appendData` (a special case, where only data can be modified). * * - The lifetime of CoordinateSystem/Axis/Scale instances: - * - They are only re-created per run of EC_FULL_UPDATE. + * - They are only re-created per run of EC_FULL_UPDATE in EC_MAIN_CYCLE. * - * - Available caches: see `cycleCache.ts` + * - Global caches: see `cycleCache.ts` */ // See comments in EC_CYCLE. @@ -1612,13 +1613,13 @@ class ECharts extends Eventful<ECEventDefinition> { seriesModel.appendData(params); - // Note: `appendData` does not support that update extent of coordinate - // system, util some scenario require that. In the expected usage of - // `appendData`, the initial extent of coordinate system should better - // be fixed by axis `min`/`max` setting or initial data, otherwise if - // the extent changed while `appendData`, the location of the painted - // graphic elements have to be changed, which make the usage of - // `appendData` meaningless. + // NOTICE: + // `appendData` does not support to update axis scale extent of coordinate + // systems. In the expected usage of `appendData`, the initial extent of + // coordinate system should be explicitly specified (by `xxxAxis.data` for + // 'category' axis or by `xxxAxis.min/max` for other axes). Otherwise, if + // the extent keep changing while `appendData`, the location of the painted + // graphic elements have to be changed frequently. this._scheduler.unfinished = true; @@ -1873,6 +1874,8 @@ class ECharts extends Eventful<ECEventDefinition> { // In LineView may save the old coordinate system and use it to get the original point. coordSysMgr.create(ecModel, api); + lifecycle.trigger('coordsys:aftercreate', ecModel, api); + scheduler.performDataProcessorTasks(ecModel, payload); // Current stream render is not supported in data process. So we can update diff --git a/src/core/lifecycle.ts b/src/core/lifecycle.ts index 933aca567..f42d8b38b 100644 --- a/src/core/lifecycle.ts +++ b/src/core/lifecycle.ts @@ -54,6 +54,7 @@ export interface UpdateLifecycleParams { } interface LifecycleEvents { 'afterinit': [EChartsType], + 'coordsys:aftercreate': [GlobalModel, ExtensionAPI], 'series:beforeupdate': [GlobalModel, ExtensionAPI, UpdateLifecycleParams], 'series:layoutlabels': [GlobalModel, ExtensionAPI, UpdateLifecycleParams], 'series:transition': [GlobalModel, ExtensionAPI, UpdateLifecycleParams], diff --git a/src/util/model.ts b/src/util/model.ts index 95d7df3d6..382eeff06 100644 --- a/src/util/model.ts +++ b/src/util/model.ts @@ -48,13 +48,12 @@ import { OptionName, InterpolatableValue, NullUndefined, - UNDEFINED_STR, } from './types'; import { Dictionary } from 'zrender/src/core/types'; import SeriesModel from '../model/Series'; import CartesianAxisModel from '../coord/cartesian/AxisModel'; import type GridModel from '../coord/cartesian/GridModel'; -import { isNumeric, getRandomIdBase, getPrecision, round, MAX_SAFE_INTEGER } from './number'; +import { isNumeric, getRandomIdBase, getPrecision, round } from './number'; import { error, warn } from './log'; import type Model from '../model/Model'; @@ -718,6 +717,10 @@ export function queryDataIndex(data: SeriesData, payload: Payload & { } /** + * [CAVEAT]: + * DO NOT use it in performance-sensitive scenarios. + * Likely a hash map lookup; not inline-cache friendly. + * * Enable property storage to any host object. * Notice: Serialization is not supported. * diff --git a/src/util/types.ts b/src/util/types.ts index aebc93ce6..c885c1353 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -385,13 +385,14 @@ export interface StageHandler { * A SERIES_STAGE_TASK is owned by a pipeline and is specific to a single series. * * The `reset` method is called iff this task is "dirty" (See `Task['dirty']`). - * Task `dirty()` call typically originates from: - * - A trigger of EC_MAIN_CYCLE (including EC_FULL_UPDATE and EC_PARTIAL_UPDATE) - * (See comments in EC_CYCLE) - * - NOTICE: `dirtyOnOverallProgress: true` cause that the corresponding `overallReset` - * and `reset` of downsteams tasks may also be called in EC_PROGRESSIVE_CYCLE. - * But in this case, `CoordinateSystem#create` and `CoordinateSystem#update` are - * not called. + * Task `dirty()` call typically originates from a trigger of EC_MAIN_CYCLE (including + * EC_FULL_UPDATE and EC_PARTIAL_UPDATE) (See comments in EC_CYCLE) + * + * NOTICE: `dirtyOnOverallProgress: true` cause that the corresponding `overallReset` + * and `reset` of downsteams tasks may also be called in EC_PROGRESSIVE_CYCLE. + * But in this case, `CoordinateSystem#create` and `CoordinateSystem#update` are not called. + * Only lifecycle like `coordsys:aftercreate` can be ensured to be only called in EC_FULL_UPDATE + * of EC_MAIN_CYCLE, but not in EC_PROGRESSIVE_CYCLE and EC_APPEND_DATA_CYCLE. */ reset?: StageHandlerReset; diff --git a/test/candlestick-large3.html b/test/candlestick-large3.html index 5f58a89b9..6c64b7afd 100644 --- a/test/candlestick-large3.html +++ b/test/candlestick-large3.html @@ -63,10 +63,11 @@ under the License. var xValueMax = rawDataChunkSize * chunkCount; var yValueMin = Infinity; var yValueMax = -Infinity; - + var xData = []; var rawData = []; + for (var i = 0; i < chunkCount; i++) { - rawData.push(generateOHLC(rawDataChunkSize)); + generateOHLC(rawDataChunkSize, rawData); } yValueMax = Math.ceil(yValueMax); yValueMin = Math.floor(yValueMin); @@ -75,7 +76,6 @@ under the License. frameInsight.init(echarts, 'duration'); - // var data = generateOHLC(rawDataChunkSize); var chart = window.chart = init(); var loadedChunkIndex = 0; @@ -99,7 +99,7 @@ under the License. } function generateOHLC(count) { - var data = []; + var seriesData = []; var tmpVals = new Array(4); var dayRange = 12; @@ -125,10 +125,12 @@ under the License. closeIdx++; } + var xValIdx = xData.length; + xData.push(echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', xValue += minute)); // ['open', 'close', 'lowest', 'highest'] // [1, 4, 3, 2] - data.push([ - echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', xValue += minute), + seriesData.push([ + xValIdx, +tmpVals[openIdx].toFixed(2), // open +tmpVals[3].toFixed(2), // highest +tmpVals[0].toFixed(2), // lowest @@ -136,7 +138,7 @@ under the License. ]); } - return data; + rawData.push(seriesData); } function calculateMA(dayCount, data) { @@ -208,8 +210,9 @@ under the License. axisLine: {onZero: false}, splitLine: {show: false}, splitNumber: 20, - min: xValueMin, - max: xValueMax + // min: xValueMin, + // max: xValueMax, + data: xData, }, // { // type: 'category', --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
