From 2244a82c37f589e51e0dfdf8b6c989ff8ffd4ffd Mon Sep 17 00:00:00 2001 From: pah100 Date: Thu, 20 Apr 2017 16:30:13 +0800 Subject: [PATCH] [dataZoom] support minSpan and maxSpan. Fix #2843. --- src/component/dataZoom/AxisProxy.js | 35 +++++++ src/component/dataZoom/DataZoomModel.js | 14 ++- src/component/dataZoom/InsideZoomView.js | 22 ++--- src/component/dataZoom/SliderZoomView.js | 18 ++-- src/component/helper/sliderMove.js | 100 +++++++++++++------- src/component/toolbox/feature/DataZoom.js | 15 ++- src/component/visualMap/ContinuousView.js | 10 +- src/coord/parallel/Parallel.js | 2 +- test/dataZoom-cartesian-h.html | 32 +++++-- test/dataZoom-toolbox.html | 2 - test/homepage3.html | 8 +- test/ut/spec/component/helper/sliderMove.js | 95 +++++++++++++++++++ test/ut/ut.js | 1 + 13 files changed, 278 insertions(+), 76 deletions(-) create mode 100644 test/ut/spec/component/helper/sliderMove.js diff --git a/src/component/dataZoom/AxisProxy.js b/src/component/dataZoom/AxisProxy.js index 52535cd94..a8c62a851 100644 --- a/src/component/dataZoom/AxisProxy.js +++ b/src/component/dataZoom/AxisProxy.js @@ -49,6 +49,13 @@ define(function(require) { */ this._dataExtent; + /** + * {minSpan, maxSpan, minValueSpan, maxValueSpan} + * @private + * @type {Object} + */ + this._minMaxSpan; + /** * @readOnly * @type {module: echarts/model/Global} @@ -147,6 +154,10 @@ define(function(require) { return foundOtherAxisModel; }, + getMinMaxSpan: function () { + return zrUtil.clone(this._minMaxSpan); + }, + /** * Only calculate by given range and this._dataExtent, do not change anything. * @@ -241,6 +252,8 @@ define(function(require) { this._valueWindow = dataWindow.valueWindow; this._percentWindow = dataWindow.percentWindow; + setMinMaxSpan(this); + // Update axis setting then. setAxisModel(this); }, @@ -400,6 +413,28 @@ define(function(require) { ); } + function setMinMaxSpan(axisProxy) { + var minMaxSpan = axisProxy._minMaxSpan = {}; + var dataZoomModel = axisProxy._dataZoomModel; + + each(['min', 'max'], function (minMax) { + minMaxSpan[minMax + 'Span'] = dataZoomModel.get(minMax + 'Span'); + + // minValueSpan and maxValueSpan has higher priority than minSpan and maxSpan + var valueSpan = dataZoomModel.get(minMax + 'ValueSpan'); + if (valueSpan != null) { + minMaxSpan[minMax + 'ValueSpan'] = valueSpan; + + valueSpan = axisProxy.getAxisModel().axis.scale.parse(valueSpan); + if (valueSpan != null) { + minMaxSpan[minMax + 'Span'] = numberUtil.linearMap( + valueSpan, axisProxy._dataExtent, [0, 100], true + ); + } + } + }); + } + return AxisProxy; }); \ No newline at end of file diff --git a/src/component/dataZoom/DataZoomModel.js b/src/component/dataZoom/DataZoomModel.js index c5aee62f2..05bb3ffe5 100644 --- a/src/component/dataZoom/DataZoomModel.js +++ b/src/component/dataZoom/DataZoomModel.js @@ -47,7 +47,11 @@ define(function(require) { 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. + 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. }, /** @@ -469,9 +473,15 @@ define(function(require) { /** * @public + * @param {module:echarts/model/Model} [axisModel] If axisModel given, find axisProxy + * corresponding to the axisModel * @return {module:echarts/component/dataZoom/AxisProxy} */ - findRepresentativeAxisProxy: function () { + findRepresentativeAxisProxy: function (axisModel) { + if (axisModel) { + return axisModel.__dzAxisProxy; + } + // Find the first hosted axisProxy var axisProxies = this._axisProxies; for (var key in axisProxies) { diff --git a/src/component/dataZoom/InsideZoomView.js b/src/component/dataZoom/InsideZoomView.js index 7f815460a..96ba73b0d 100644 --- a/src/component/dataZoom/InsideZoomView.js +++ b/src/component/dataZoom/InsideZoomView.js @@ -98,7 +98,7 @@ define(function (require) { * (range[1] - range[0]) * directionInfo.pixel / directionInfo.pixelLength; - sliderMove(percentDelta, range, [0, 100], 'rigid'); + sliderMove(percentDelta, range, [0, 100], 'all'); return (this._range = range); }, @@ -131,7 +131,12 @@ define(function (require) { scale = Math.max(1 / scale, 0); range[0] = (range[0] - percentPoint) * scale + percentPoint; range[1] = (range[1] - percentPoint) * scale + percentPoint; - return (this._range = fixRange(range)); + + // Restrict range. + var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); + sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan); + + return (this._range = range); } }); @@ -214,18 +219,5 @@ define(function (require) { } }; - function fixRange(range) { - // Clamp, using !(<= or >=) to handle NaN. - // jshint ignore:start - var bound = [0, 100]; - !(range[0] <= bound[1]) && (range[0] = bound[1]); - !(range[1] <= bound[1]) && (range[1] = bound[1]); - !(range[0] >= bound[0]) && (range[0] = bound[0]); - !(range[1] >= bound[0]) && (range[1] = bound[0]); - // jshint ignore:end - - return range; - } - return InsideZoomView; }); \ No newline at end of file diff --git a/src/component/dataZoom/SliderZoomView.js b/src/component/dataZoom/SliderZoomView.js index 1f0b4b651..dff719a2c 100644 --- a/src/component/dataZoom/SliderZoomView.js +++ b/src/component/dataZoom/SliderZoomView.js @@ -520,25 +520,29 @@ define(function (require) { /** * @private * @param {(number|string)} handleIndex 0 or 1 or 'all' - * @param {number} dx - * @param {number} dy + * @param {number} delta */ _updateInterval: function (handleIndex, delta) { + var dataZoomModel = this.dataZoomModel; var handleEnds = this._handleEnds; var viewExtend = this._getViewExtent(); + var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); + var percentExtent = [0, 100]; sliderMove( delta, handleEnds, viewExtend, - (handleIndex === 'all' || this.dataZoomModel.get('zoomLock')) - ? 'rigid' : 'cross', - handleIndex + dataZoomModel.get('zoomLock') ? 'all' : handleIndex, + minMaxSpan.minSpan != null + ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, + minMaxSpan.maxSpan != null + ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null ); this._range = asc([ - linearMap(handleEnds[0], viewExtend, [0, 100], true), - linearMap(handleEnds[1], viewExtend, [0, 100], true) + linearMap(handleEnds[0], viewExtend, percentExtent, true), + linearMap(handleEnds[1], viewExtend, percentExtent, true) ]); }, diff --git a/src/component/helper/sliderMove.js b/src/component/helper/sliderMove.js index c51d34b46..39902d388 100644 --- a/src/component/helper/sliderMove.js +++ b/src/component/helper/sliderMove.js @@ -2,53 +2,81 @@ define(function (require) { /** * Calculate slider move result. + * Usage: + * (1) If both handle0 and handle1 are needed to be moved, set minSpan the same as + * maxSpan and the same as `Math.abs(handleEnd[1] - handleEnds[0])`. + * (2) If handle0 is forbidden to cross handle1, set minSpan as `0`. * * @param {number} delta Move length. - * @param {Array.} handleEnds handleEnds[0] and be bigger then handleEnds[1]. - * handleEnds will be modified in this method. + * @param {Array.} handleEnds handleEnds[0] can be bigger then handleEnds[1]. + * handleEnds will be modified in this method. * @param {Array.} extent handleEnds is restricted by extent. - * extent[0] should less or equals than extent[1]. - * @param {string} mode 'rigid': Math.abs(handleEnds[0] - handleEnds[1]) remain unchanged, - * 'cross' handleEnds[0] can be bigger then handleEnds[1], - * 'push' handleEnds[0] can not be bigger then handleEnds[1], - * when they touch, one push other. - * @param {number} handleIndex If mode is 'rigid', handleIndex is not required. + * extent[0] should less or equals than extent[1]. + * @param {number|string} handleIndex Can be 'all', means that both move the two handleEnds, + * where the input minSpan and maxSpan will not work. + * @param {number} [minSpan] The range of dataZoom can not be smaller than that. + * If not set, handle0 and cross handle1. If set as a non-negative + * number (including `0`), handles will push each other when reaching + * the minSpan. + * @param {number} [maxSpan] The range of dataZoom can not be larger than that. * @return {Array.} The input handleEnds. */ - return function (delta, handleEnds, extent, mode, handleIndex) { - if (!delta) { - return handleEnds; + return function (delta, handleEnds, extent, handleIndex, minSpan, maxSpan) { + // Normalize firstly. + restrict(handleEnds, 0, extent); + restrict(handleEnds, 1, extent); + + delta = delta || 0; + + var extentSpan = extent[1] - extent[0]; + + // Notice maxSpan and minSpan can be null/undefined. + minSpan < 0 && (minSpan = 0); + minSpan > extentSpan && (minSpan = extentSpan); + if (maxSpan < minSpan) { + maxSpan = minSpan; } + if (handleIndex === 'all') { + minSpan = maxSpan = Math.abs(handleEnds[1] - handleEnds[0]); + handleIndex = 0; + } + + var originalDistSign = getSpanSign(handleEnds, handleIndex); + + handleEnds[handleIndex] += delta; - if (mode === 'rigid') { - delta = getRealDelta(delta, handleEnds, extent); - handleEnds[0] += delta; - handleEnds[1] += delta; + // Restrict in extent. + var extentMinSpan = minSpan || 0; + var realExtent = extent.slice(); + originalDistSign.sign < 0 ? (realExtent[0] += extentMinSpan) : (realExtent[1] -= extentMinSpan); + restrict(handleEnds, handleIndex, realExtent); + + // Expand span. + var currDistSign = getSpanSign(handleEnds, handleIndex); + if (minSpan != null && ( + currDistSign.sign !== originalDistSign.sign || currDistSign.span < minSpan + )) { + // If minSpan exists, 'cross' is forbinden. + handleEnds[1 - handleIndex] = handleEnds[handleIndex] + originalDistSign.sign * minSpan; } - else { - delta = getRealDelta(delta, handleEnds[handleIndex], extent); - handleEnds[handleIndex] += delta; - if (mode === 'push' && handleEnds[0] > handleEnds[1]) { - handleEnds[1 - handleIndex] = handleEnds[handleIndex]; - } + // Shrink span. + var currDistSign = getSpanSign(handleEnds, handleIndex); + if (maxSpan != null && currDistSign.span > maxSpan) { + handleEnds[1 - handleIndex] = handleEnds[handleIndex] + currDistSign.sign * maxSpan; } return handleEnds; - - function getRealDelta(delta, handleEnds, extent) { - var handleMinMax = !handleEnds.length - ? [handleEnds, handleEnds] - : handleEnds.slice(); - handleEnds[0] > handleEnds[1] && handleMinMax.reverse(); - - if (delta < 0 && handleMinMax[0] + delta < extent[0]) { - delta = extent[0] - handleMinMax[0]; - } - if (delta > 0 && handleMinMax[1] + delta > extent[1]) { - delta = extent[1] - handleMinMax[1]; - } - return delta; - } }; + + function getSpanSign(handleEnds, handleIndex) { + var dist = handleEnds[handleIndex] - handleEnds[1 - handleIndex]; + // If `handleEnds[0] === handleEnds[1]`, always believe that handleEnd[0] + // is at left of handleEnds[1] for non-cross case. + return {span: Math.abs(dist), sign: dist > 0 ? -1 : dist < 0 ? 1 : handleIndex ? -1 : 1}; + } + + function restrict(handleEnds, handleIndex, extend) { + handleEnds[handleIndex] = Math.min(extend[1], Math.max(extend[0], handleEnds[handleIndex])); + } }); \ No newline at end of file diff --git a/src/component/toolbox/feature/DataZoom.js b/src/component/toolbox/feature/DataZoom.js index aa5368ab3..d4ac86795 100644 --- a/src/component/toolbox/feature/DataZoom.js +++ b/src/component/toolbox/feature/DataZoom.js @@ -5,6 +5,7 @@ define(function(require) { var BrushController = require('../../helper/BrushController'); var BrushTargetManager = require('../../helper/BrushTargetManager'); var history = require('../../dataZoom/history'); + var sliderMove = require('../../helper/sliderMove'); var each = zrUtil.each; @@ -122,7 +123,19 @@ define(function(require) { this._dispatchZoomAction(snapshot); function setBatch(dimName, coordSys, minMax) { - var dataZoomModel = findDataZoom(dimName, coordSys.getAxis(dimName).model, ecModel); + var axis = coordSys.getAxis(dimName); + var axisModel = axis.model; + var dataZoomModel = findDataZoom(dimName, axisModel, ecModel); + + // Restrict range. + var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan(); + if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) { + minMax = sliderMove( + 0, minMax.slice(), axis.scale.getExtent(), 0, + minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan + ); + } + dataZoomModel && (snapshot[dataZoomModel.id] = { dataZoomId: dataZoomModel.id, startValue: minMax[0], diff --git a/src/component/visualMap/ContinuousView.js b/src/component/visualMap/ContinuousView.js index d057556ae..2b971b9b0 100644 --- a/src/component/visualMap/ContinuousView.js +++ b/src/component/visualMap/ContinuousView.js @@ -355,16 +355,18 @@ define(function(require) { delta = delta || 0; var visualMapModel = this.visualMapModel; var handleEnds = this._handleEnds; + var sizeExtent = [0, visualMapModel.itemSize[1]]; sliderMove( delta, handleEnds, - [0, visualMapModel.itemSize[1]], - handleIndex === 'all' ? 'rigid' : 'push', - handleIndex + sizeExtent, + handleIndex, + // cross is forbiden + 0 ); + var dataExtent = visualMapModel.getExtent(); - var sizeExtent = [0, visualMapModel.itemSize[1]]; // Update data interval. this._dataInterval = [ linearMap(handleEnds[0], sizeExtent, dataExtent, true), diff --git a/src/coord/parallel/Parallel.js b/src/coord/parallel/Parallel.js index ec1c71ca8..db9f0a3a0 100644 --- a/src/coord/parallel/Parallel.js +++ b/src/coord/parallel/Parallel.js @@ -443,7 +443,7 @@ define(function(require) { } delta *= layoutInfo.axisExpandWidth / axisCollapseWidth; delta - ? sliderMove(delta, axisExpandWindow, extent, 'rigid') + ? sliderMove(delta, axisExpandWindow, extent, 'all') // Avoid nonsense triger on mousemove. : (behavior = 'none'); } diff --git a/test/dataZoom-cartesian-h.html b/test/dataZoom-cartesian-h.html index 0374355ec..db4d3b573 100644 --- a/test/dataZoom-cartesian-h.html +++ b/test/dataZoom-cartesian-h.html @@ -6,9 +6,12 @@ -

Check the highlighted label while data zooming

+

+ Check the highlighted label while data zooming.
+ Check the minSpan and maxSpan (both in slider and inside and toolbox zoom). +