123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- var _config = require("../../config");
- var __DEV__ = _config.__DEV__;
- var echarts = require("../../echarts");
- var zrUtil = require("zrender/lib/core/util");
- var env = require("zrender/lib/core/env");
- var modelUtil = require("../../util/model");
- var helper = require("./helper");
- var AxisProxy = require("./AxisProxy");
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- var each = zrUtil.each;
- var eachAxisDim = helper.eachAxisDim;
- var DataZoomModel = echarts.extendComponentModel({
- type: 'dataZoom',
- dependencies: ['xAxis', 'yAxis', 'zAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'series'],
- /**
- * @protected
- */
- defaultOption: {
- zlevel: 0,
- z: 4,
- // Higher than normal component (z: 2).
- orient: null,
- // Default auto by axisIndex. Possible value: 'horizontal', 'vertical'.
- xAxisIndex: null,
- // Default the first horizontal category axis.
- yAxisIndex: null,
- // Default the first vertical category axis.
- filterMode: 'filter',
- // Possible values: 'filter' or 'empty' or 'weakFilter'.
- // 'filter': data items which are out of window will be removed. This option is
- // applicable when filtering outliers. For each data item, it will be
- // filtered if one of the relevant dimensions is out of the window.
- // 'weakFilter': data items which are out of window will be removed. This option
- // is applicable when filtering outliers. For each data item, it will be
- // filtered only if all of the relevant dimensions are out of the same
- // side of the window.
- // 'empty': data items which are out of window will be set to empty.
- // This option is applicable when user should not neglect
- // that there are some data items out of window.
- // 'none': Do not filter.
- // Taking line chart as an example, line will be broken in
- // the filtered points when filterModel is set to 'empty', but
- // be connected when set to 'filter'.
- throttle: null,
- // Dispatch action by the fixed rate, avoid frequency.
- // default 100. Do not throttle when use null/undefined.
- // If animation === true and animationDurationUpdate > 0,
- // default value is 100, otherwise 20.
- start: 0,
- // Start percent. 0 ~ 100
- end: 100,
- // End percent. 0 ~ 100
- startValue: null,
- // Start value. If startValue specified, start is ignored.
- endValue: null,
- // End value. If endValue specified, end is ignored.
- minSpan: null,
- // 0 ~ 100
- maxSpan: null,
- // 0 ~ 100
- minValueSpan: null,
- // The range of dataZoom can not be smaller than that.
- maxValueSpan: null,
- // The range of dataZoom can not be larger than that.
- rangeMode: null // Array, can be 'value' or 'percent'.
- },
- /**
- * @override
- */
- init: function (option, parentModel, ecModel) {
- /**
- * key like x_0, y_1
- * @private
- * @type {Object}
- */
- this._dataIntervalByAxis = {};
- /**
- * @private
- */
- this._dataInfo = {};
- /**
- * key like x_0, y_1
- * @private
- */
- this._axisProxies = {};
- /**
- * @readOnly
- */
- this.textStyleModel;
- /**
- * @private
- */
- this._autoThrottle = true;
- /**
- * It is `[rangeModeForMin, rangeModeForMax]`.
- * The optional values for `rangeMode`:
- * + `'value'` mode: the axis extent will always be determined by
- * `dataZoom.startValue` and `dataZoom.endValue`, despite
- * how data like and how `axis.min` and `axis.max` are.
- * + `'percent'` mode: `100` represents 100% of the `[dMin, dMax]`,
- * where `dMin` is `axis.min` if `axis.min` specified, otherwise `data.extent[0]`,
- * and `dMax` is `axis.max` if `axis.max` specified, otherwise `data.extent[1]`.
- * Axis extent will be determined by the result of the percent of `[dMin, dMax]`.
- *
- * For example, when users are using dynamic data (update data periodically via `setOption`),
- * if in `'value`' mode, the window will be kept in a fixed value range despite how
- * data are appended, while if in `'percent'` mode, whe window range will be changed alone with
- * the appended data (suppose `axis.min` and `axis.max` are not specified).
- *
- * @private
- */
- this._rangePropMode = ['percent', 'percent'];
- var inputRawOption = retrieveRawOption(option);
- /**
- * Suppose a "main process" start at the point that model prepared (that is,
- * model initialized or merged or method called in `action`).
- * We should keep the `main process` idempotent, that is, given a set of values
- * on `option`, we get the same result.
- *
- * But sometimes, values on `option` will be updated for providing users
- * a "final calculated value" (`dataZoomProcessor` will do that). Those value
- * should not be the base/input of the `main process`.
- *
- * So in that case we should save and keep the input of the `main process`
- * separately, called `settledOption`.
- *
- * For example, consider the case:
- * (Step_1) brush zoom the grid by `toolbox.dataZoom`,
- * where the original input `option.startValue`, `option.endValue` are earsed by
- * calculated value.
- * (Step)2) click the legend to hide and show a series,
- * where the new range is calculated by the earsed `startValue` and `endValue`,
- * which brings incorrect result.
- *
- * @readOnly
- */
- this.settledOption = inputRawOption;
- this.mergeDefaultAndTheme(option, ecModel);
- this.doInit(inputRawOption);
- },
- /**
- * @override
- */
- mergeOption: function (newOption) {
- var inputRawOption = retrieveRawOption(newOption); //FIX #2591
- zrUtil.merge(this.option, newOption, true);
- zrUtil.merge(this.settledOption, inputRawOption, true);
- this.doInit(inputRawOption);
- },
- /**
- * @protected
- */
- doInit: function (inputRawOption) {
- var thisOption = this.option; // Disable realtime view update if canvas is not supported.
- if (!env.canvasSupported) {
- thisOption.realtime = false;
- }
- this._setDefaultThrottle(inputRawOption);
- updateRangeUse(this, inputRawOption);
- var settledOption = this.settledOption;
- each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {
- // start/end has higher priority over startValue/endValue if they
- // both set, but we should make chart.setOption({endValue: 1000})
- // effective, rather than chart.setOption({endValue: 1000, end: null}).
- if (this._rangePropMode[index] === 'value') {
- thisOption[names[0]] = settledOption[names[0]] = null;
- } // Otherwise do nothing and use the merge result.
- }, this);
- this.textStyleModel = this.getModel('textStyle');
- this._resetTarget();
- this._giveAxisProxies();
- },
- /**
- * @private
- */
- _giveAxisProxies: function () {
- var axisProxies = this._axisProxies;
- this.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel, ecModel) {
- var axisModel = this.dependentModels[dimNames.axis][axisIndex]; // If exists, share axisProxy with other dataZoomModels.
- var axisProxy = axisModel.__dzAxisProxy || ( // Use the first dataZoomModel as the main model of axisProxy.
- axisModel.__dzAxisProxy = new AxisProxy(dimNames.name, axisIndex, this, ecModel)); // FIXME
- // dispose __dzAxisProxy
- axisProxies[dimNames.name + '_' + axisIndex] = axisProxy;
- }, this);
- },
- /**
- * @private
- */
- _resetTarget: function () {
- var thisOption = this.option;
- var autoMode = this._judgeAutoMode();
- eachAxisDim(function (dimNames) {
- var axisIndexName = dimNames.axisIndex;
- thisOption[axisIndexName] = modelUtil.normalizeToArray(thisOption[axisIndexName]);
- }, this);
- if (autoMode === 'axisIndex') {
- this._autoSetAxisIndex();
- } else if (autoMode === 'orient') {
- this._autoSetOrient();
- }
- },
- /**
- * @private
- */
- _judgeAutoMode: function () {
- // Auto set only works for setOption at the first time.
- // The following is user's reponsibility. So using merged
- // option is OK.
- var thisOption = this.option;
- var hasIndexSpecified = false;
- eachAxisDim(function (dimNames) {
- // When user set axisIndex as a empty array, we think that user specify axisIndex
- // but do not want use auto mode. Because empty array may be encountered when
- // some error occured.
- if (thisOption[dimNames.axisIndex] != null) {
- hasIndexSpecified = true;
- }
- }, this);
- var orient = thisOption.orient;
- if (orient == null && hasIndexSpecified) {
- return 'orient';
- } else if (!hasIndexSpecified) {
- if (orient == null) {
- thisOption.orient = 'horizontal';
- }
- return 'axisIndex';
- }
- },
- /**
- * @private
- */
- _autoSetAxisIndex: function () {
- var autoAxisIndex = true;
- var orient = this.get('orient', true);
- var thisOption = this.option;
- var dependentModels = this.dependentModels;
- if (autoAxisIndex) {
- // Find axis that parallel to dataZoom as default.
- var dimName = orient === 'vertical' ? 'y' : 'x';
- if (dependentModels[dimName + 'Axis'].length) {
- thisOption[dimName + 'AxisIndex'] = [0];
- autoAxisIndex = false;
- } else {
- each(dependentModels.singleAxis, function (singleAxisModel) {
- if (autoAxisIndex && singleAxisModel.get('orient', true) === orient) {
- thisOption.singleAxisIndex = [singleAxisModel.componentIndex];
- autoAxisIndex = false;
- }
- });
- }
- }
- if (autoAxisIndex) {
- // Find the first category axis as default. (consider polar)
- eachAxisDim(function (dimNames) {
- if (!autoAxisIndex) {
- return;
- }
- var axisIndices = [];
- var axisModels = this.dependentModels[dimNames.axis];
- if (axisModels.length && !axisIndices.length) {
- for (var i = 0, len = axisModels.length; i < len; i++) {
- if (axisModels[i].get('type') === 'category') {
- axisIndices.push(i);
- }
- }
- }
- thisOption[dimNames.axisIndex] = axisIndices;
- if (axisIndices.length) {
- autoAxisIndex = false;
- }
- }, this);
- }
- if (autoAxisIndex) {
- // FIXME
- // 这里是兼容ec2的写法(没指定xAxisIndex和yAxisIndex时把scatter和双数值轴折柱纳入dataZoom控制),
- // 但是实际是否需要Grid.js#getScaleByOption来判断(考虑time,log等axis type)?
- // If both dataZoom.xAxisIndex and dataZoom.yAxisIndex is not specified,
- // dataZoom component auto adopts series that reference to
- // both xAxis and yAxis which type is 'value'.
- this.ecModel.eachSeries(function (seriesModel) {
- if (this._isSeriesHasAllAxesTypeOf(seriesModel, 'value')) {
- eachAxisDim(function (dimNames) {
- var axisIndices = thisOption[dimNames.axisIndex];
- var axisIndex = seriesModel.get(dimNames.axisIndex);
- var axisId = seriesModel.get(dimNames.axisId);
- var axisModel = seriesModel.ecModel.queryComponents({
- mainType: dimNames.axis,
- index: axisIndex,
- id: axisId
- })[0];
- axisIndex = axisModel.componentIndex;
- if (zrUtil.indexOf(axisIndices, axisIndex) < 0) {
- axisIndices.push(axisIndex);
- }
- });
- }
- }, this);
- }
- },
- /**
- * @private
- */
- _autoSetOrient: function () {
- var dim; // Find the first axis
- this.eachTargetAxis(function (dimNames) {
- !dim && (dim = dimNames.name);
- }, this);
- this.option.orient = dim === 'y' ? 'vertical' : 'horizontal';
- },
- /**
- * @private
- */
- _isSeriesHasAllAxesTypeOf: function (seriesModel, axisType) {
- // FIXME
- // 需要series的xAxisIndex和yAxisIndex都首先自动设置上。
- // 例如series.type === scatter时。
- var is = true;
- eachAxisDim(function (dimNames) {
- var seriesAxisIndex = seriesModel.get(dimNames.axisIndex);
- var axisModel = this.dependentModels[dimNames.axis][seriesAxisIndex];
- if (!axisModel || axisModel.get('type') !== axisType) {
- is = false;
- }
- }, this);
- return is;
- },
- /**
- * @private
- */
- _setDefaultThrottle: function (inputRawOption) {
- // When first time user set throttle, auto throttle ends.
- if (inputRawOption.hasOwnProperty('throttle')) {
- this._autoThrottle = false;
- }
- if (this._autoThrottle) {
- var globalOption = this.ecModel.option;
- this.option.throttle = globalOption.animation && globalOption.animationDurationUpdate > 0 ? 100 : 20;
- }
- },
- /**
- * @public
- */
- getFirstTargetAxisModel: function () {
- var firstAxisModel;
- eachAxisDim(function (dimNames) {
- if (firstAxisModel == null) {
- var indices = this.get(dimNames.axisIndex);
- if (indices.length) {
- firstAxisModel = this.dependentModels[dimNames.axis][indices[0]];
- }
- }
- }, this);
- return firstAxisModel;
- },
- /**
- * @public
- * @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel
- */
- eachTargetAxis: function (callback, context) {
- var ecModel = this.ecModel;
- eachAxisDim(function (dimNames) {
- each(this.get(dimNames.axisIndex), function (axisIndex) {
- callback.call(context, dimNames, axisIndex, this, ecModel);
- }, this);
- }, this);
- },
- /**
- * @param {string} dimName
- * @param {number} axisIndex
- * @return {module:echarts/component/dataZoom/AxisProxy} If not found, return null/undefined.
- */
- getAxisProxy: function (dimName, axisIndex) {
- return this._axisProxies[dimName + '_' + axisIndex];
- },
- /**
- * @param {string} dimName
- * @param {number} axisIndex
- * @return {module:echarts/model/Model} If not found, return null/undefined.
- */
- getAxisModel: function (dimName, axisIndex) {
- var axisProxy = this.getAxisProxy(dimName, axisIndex);
- return axisProxy && axisProxy.getAxisModel();
- },
- /**
- * If not specified, set to undefined.
- *
- * @public
- * @param {Object} opt
- * @param {number} [opt.start]
- * @param {number} [opt.end]
- * @param {number} [opt.startValue]
- * @param {number} [opt.endValue]
- */
- setRawRange: function (opt) {
- var thisOption = this.option;
- var settledOption = this.settledOption;
- each([['start', 'startValue'], ['end', 'endValue']], function (names) {
- // Consider the pair <start, startValue>:
- // If one has value and the other one is `null/undefined`, we both set them
- // to `settledOption`. This strategy enables the feature to clear the original
- // value in `settledOption` to `null/undefined`.
- // But if both of them are `null/undefined`, we do not set them to `settledOption`
- // and keep `settledOption` with the original value. This strategy enables users to
- // only set <end or endValue> but not set <start or startValue> when calling
- // `dispatchAction`.
- // The pair <end, endValue> is treated in the same way.
- if (opt[names[0]] != null || opt[names[1]] != null) {
- thisOption[names[0]] = settledOption[names[0]] = opt[names[0]];
- thisOption[names[1]] = settledOption[names[1]] = opt[names[1]];
- }
- }, this);
- updateRangeUse(this, opt);
- },
- /**
- * @public
- * @param {Object} opt
- * @param {number} [opt.start]
- * @param {number} [opt.end]
- * @param {number} [opt.startValue]
- * @param {number} [opt.endValue]
- */
- setCalculatedRange: function (opt) {
- var option = this.option;
- each(['start', 'startValue', 'end', 'endValue'], function (name) {
- option[name] = opt[name];
- });
- },
- /**
- * @public
- * @return {Array.<number>} [startPercent, endPercent]
- */
- getPercentRange: function () {
- var axisProxy = this.findRepresentativeAxisProxy();
- if (axisProxy) {
- return axisProxy.getDataPercentWindow();
- }
- },
- /**
- * @public
- * For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0);
- *
- * @param {string} [axisDimName]
- * @param {number} [axisIndex]
- * @return {Array.<number>} [startValue, endValue] value can only be '-' or finite number.
- */
- getValueRange: function (axisDimName, axisIndex) {
- if (axisDimName == null && axisIndex == null) {
- var axisProxy = this.findRepresentativeAxisProxy();
- if (axisProxy) {
- return axisProxy.getDataValueWindow();
- }
- } else {
- return this.getAxisProxy(axisDimName, axisIndex).getDataValueWindow();
- }
- },
- /**
- * @public
- * @param {module:echarts/model/Model} [axisModel] If axisModel given, find axisProxy
- * corresponding to the axisModel
- * @return {module:echarts/component/dataZoom/AxisProxy}
- */
- findRepresentativeAxisProxy: function (axisModel) {
- if (axisModel) {
- return axisModel.__dzAxisProxy;
- } // Find the first hosted axisProxy
- var axisProxies = this._axisProxies;
- for (var key in axisProxies) {
- if (axisProxies.hasOwnProperty(key) && axisProxies[key].hostedBy(this)) {
- return axisProxies[key];
- }
- } // If no hosted axis find not hosted axisProxy.
- // Consider this case: dataZoomModel1 and dataZoomModel2 control the same axis,
- // and the option.start or option.end settings are different. The percentRange
- // should follow axisProxy.
- // (We encounter this problem in toolbox data zoom.)
- for (var key in axisProxies) {
- if (axisProxies.hasOwnProperty(key) && !axisProxies[key].hostedBy(this)) {
- return axisProxies[key];
- }
- }
- },
- /**
- * @return {Array.<string>}
- */
- getRangePropMode: function () {
- return this._rangePropMode.slice();
- }
- });
- /**
- * Retrieve the those raw params from option, which will be cached separately.
- * becasue they will be overwritten by normalized/calculated values in the main
- * process.
- */
- function retrieveRawOption(option) {
- var ret = {};
- each(['start', 'end', 'startValue', 'endValue', 'throttle'], function (name) {
- option.hasOwnProperty(name) && (ret[name] = option[name]);
- });
- return ret;
- }
- function updateRangeUse(dataZoomModel, inputRawOption) {
- var rangePropMode = dataZoomModel._rangePropMode;
- var rangeModeInOption = dataZoomModel.get('rangeMode');
- each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {
- var percentSpecified = inputRawOption[names[0]] != null;
- var valueSpecified = inputRawOption[names[1]] != null;
- if (percentSpecified && !valueSpecified) {
- rangePropMode[index] = 'percent';
- } else if (!percentSpecified && valueSpecified) {
- rangePropMode[index] = 'value';
- } else if (rangeModeInOption) {
- rangePropMode[index] = rangeModeInOption[index];
- } else if (percentSpecified) {
- // percentSpecified && valueSpecified
- rangePropMode[index] = 'percent';
- } // else remain its original setting.
- });
- }
- var _default = DataZoomModel;
- module.exports = _default;
|