提交 2244a82c 编写于 作者: P pah100

[dataZoom] support minSpan and maxSpan. Fix #2843.

上级 74f15c1d
......@@ -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
......@@ -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) {
......
......@@ -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
......@@ -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)
]);
},
......
......@@ -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.<number>} handleEnds handleEnds[0] and be bigger then handleEnds[1].
* handleEnds will be modified in this method.
* @param {Array.<number>} handleEnds handleEnds[0] can be bigger then handleEnds[1].
* handleEnds will be modified in this method.
* @param {Array.<number>} 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.<number>} 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
......@@ -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],
......
......@@ -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),
......
......@@ -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');
}
......
......@@ -6,9 +6,12 @@
</head>
<body>
<style>
html, body, #main {
width: 100%;
height: 100%;
html, body{
padding: 0;
margin: 0;
}
#main {
height: 500px;
}
body {
margin: 0;
......@@ -19,7 +22,10 @@
line-height: 30px;
}
</style>
<h3>Check the highlighted label while data zooming</h3>
<h3>
Check the highlighted label while data zooming.<br>
Check the minSpan and maxSpan (both in slider and inside and toolbox zoom).
</h3>
<div id="main"></div>
<script>
......@@ -32,6 +38,7 @@
'echarts/component/axis',
'echarts/component/dataZoom',
'echarts/component/tooltip',
'echarts/component/toolbox',
'echarts/component/markPoint',
'echarts/component/markLine'
], function (echarts) {
......@@ -96,6 +103,15 @@
tooltip: {
trigger: 'axis'
},
toolbox: {
feature: {
dataZoom: {
show: true,
xAxisIndex: false
},
restore: {show: true}
}
},
yAxis: {
data: xAxisData,
boundaryGap: false
......@@ -153,13 +169,17 @@
end: 30,
borderColor: 'rgba(0,0,0,0.15)',
backgroundColor: 'rgba(200,200,200,0)',
yAxisIndex: 0
yAxisIndex: 0,
minValueSpan: 5,
maxValueSpan: 80
},
{
type: 'inside',
startValue: 2,
end: 30,
yAxisIndex: 0
yAxisIndex: 0,
minSpan: 5,
maxSpan: 80
}
]
});
......
......@@ -11,8 +11,6 @@
</head>
<body>
<style>
body {
}
.chart {
position: relative;
height: 500px;
......
......@@ -59,6 +59,9 @@
</div>
<script type="text/javascript">
var data = {"years":[1800,1810,1820,1830,1840,1850,1860,1870,1880,1890,1900,1910,1920,1930,1940,1950,1951,1952,1953,1954,1955,1956,1957,1958,1959,1960,1961,1962,1963,1964,1965,1966,1967,1968,1969,1970,1971,1972,1973,1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984,1985,1986,1987,1988,1989,1990,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015],"lifeExpectancy":[29.5734572,29.5734572,29.5734572,29.5734572,29.5734572,29.5734572,29.5734572,31.12082604,30.20106663,29.93047652,30.74960789,31.40217766,20.5,36.428,41.056,57.084,57.5768,57.9696,58.7624,60.7552,63.148,64.6408,63.7336,66.6264,67.3692,68.382,68.6248,68.2776,68.7404,69.5332,69.116,69.1788,68.9616,68.9144,68.3872,68.5,68.6,68.7,68.7,68.6,68.2,68,67.8,67.7,67.4,67.3,67.5,67.9,67.7,67.4,68.2,69.8,70.1,70,69.8,69.6,69.4,68,65.2,63.6,64.2,65.9,67.4,67.6,66.2,65.4,65.1,64.9,64.8,65,64.8,66.1,67.2,67.6,68.3,68.7,69.4,70.4,71.3,72.21,73.13],"income":[1430,1447,1464,1562,1666,1778,1896,2023,2158,2233,3087,3487,1489,3779,5632,7514,7424,7775,7981,8234,8787,9465,9496,10037,9755,10496,10908,11027,10620,11836,12363,12823,13256,13902,13972,14915,15170,15113,16236,16594,16530,17192,17487,17818,17632,17557,17619,17951,18417,18527,18576,19221,19355,19660,19906,19349,18332,15661,14320,12535,12013,11597,11779,11173,11925,13173,13902,14629,15768,16967,18118,19660,21374,22506,20739,21664,22570,23299,23561,23293,23038],"population":[31088398,31088398,31861526,34134430,37420913,41023821,44966686,49288504,53996807,59151534,64836675,71044207,77871987,85369549,93588981,102798657,104306354,105969442,107729541,109537868,111355224,113152347,114909562,116615781,118266807,119860289,121390327,122842753,124193114,125412397,126483874,127396324,128165823,128837792,129475269,130126383,130808492,131517584,132254362,133012558,133788113,134583945,135406786,136259517,137144808,138063062,139006739,139969243,140951400,141955200,142975753,144016095,145056221,146040116,146895053,147568552,148040354,148322473,148435811,148416292,148293265,148078355,147772805,147385440,146924174,146400951,145818121,145195521,144583147,144043914,143622566,143338407,143180249,143123163,143126660,143158099,143211476,143287536,143367341,143429435,143456918]};
var series1Color = '#c23531';
......@@ -692,11 +695,12 @@
});
fromChart.on('dataZoom', function (params) {
var payload = params.batch ? params.batch[0] : params;
toChart.dispatchAction({
type: 'dataZoom',
dataZoomIndex: 0,
start: params.batch[0].start,
end: params.batch[0].end
start: payload.start,
end: payload.end
}, true);
});
}
......
describe('component/helper/sliderMove', function () {
var utHelper = window.utHelper;
var testCase = utHelper.prepare(['echarts/component/helper/sliderMove']);
describe('sliderMove', function () {
testCase('normalize', function (sliderMove) {
// Return input handleEnds
var inputHandleEnds = [22, 50];
var outputHandleEnds = sliderMove(0, inputHandleEnds, [20, 50], 0);
expect(inputHandleEnds === outputHandleEnds).toEqual(true);
expect(outputHandleEnds).toEqual([22, 50]);
// delta 0 and normalize
expect(sliderMove(0, [-10, 70], [20, 50], 0)).toEqual([20, 50]);
// normalize by minSpec
expect(sliderMove(0, [20, 22], [20, 50], 0, 10)).toEqual([20, 30]);
// normalize by maxSpec
expect(sliderMove(0, [20, 42], [20, 50], 0, null, 10)).toEqual([20, 30]);
// Do not move
expect(sliderMove(0, [-10, 70], [20, 50], 'all')).toEqual([20, 50]);
// minSpan bigger than extent
expect(sliderMove(4, [20, 25], [10, 50], 0, 300)).toEqual([10, 50]);
// maxSpan smaller than minSpan
expect(sliderMove(4, [20, 25], [10, 50], 0, 6, 3)).toEqual([24, 30]);
});
testCase('rigid_move', function (sliderMove) {
expect(sliderMove(2, [20, 30], [10, 50], 'all')).toEqual([22, 32]);
expect(sliderMove(200, [20, 30], [10, 50], 'all')).toEqual([40, 50]);
expect(sliderMove(-2, [30, 40], [10, 50], 'all')).toEqual([28, 38]);
expect(sliderMove(-2, [10, 20], [10, 50], 'all')).toEqual([10, 20]);
});
testCase('cross', function (sliderMove) {
expect(sliderMove(2, [20, 25], [10, 50], 0)).toEqual([22, 25]);
expect(sliderMove(200, [20, 25], [10, 50], 0)).toEqual([50, 25]);
expect(sliderMove(-2, [20, 25], [10, 50], 0)).toEqual([18, 25]);
expect(sliderMove(-200, [20, 25], [10, 50], 0)).toEqual([10, 25]);
expect(sliderMove(2, [20, 25], [10, 50], 1)).toEqual([20, 27]);
expect(sliderMove(200, [20, 25], [10, 50], 1)).toEqual([20, 50]);
expect(sliderMove(-2, [20, 25], [10, 50], 1)).toEqual([20, 23]);
expect(sliderMove(-200, [20, 25], [10, 50], 1)).toEqual([20, 10]);
});
testCase('minSpan_push', function (sliderMove) {
expect(sliderMove(1, [20, 25], [10, 50], 0, 3)).toEqual([21, 25]);
expect(sliderMove(4, [20, 25], [10, 50], 0, 3)).toEqual([24, 27]);
expect(sliderMove(200, [20, 25], [10, 50], 0, 3)).toEqual([47, 50]);
expect(sliderMove(-200, [20, 25], [10, 50], 0, 3)).toEqual([10, 25]);
expect(sliderMove(-1, [20, 25], [10, 50], 1, 3)).toEqual([20, 24]);
expect(sliderMove(-4, [20, 25], [10, 50], 1, 3)).toEqual([18, 21]);
expect(sliderMove(-200, [20, 25], [10, 50], 1, 3)).toEqual([10, 13]);
expect(sliderMove(200, [20, 25], [10, 50], 1, 3)).toEqual([20, 50]);
// minSpan is 0.
expect(sliderMove(10, [20, 25], [10, 50], 0, 0)).toEqual([30, 30]);
expect(sliderMove(-10, [20, 25], [10, 50], 1, 0)).toEqual([15, 15]);
// Input handleEnds[0] === handleEnds[1], should not cross.
expect(sliderMove(10, [20, 20], [10, 50], 0, 0)).toEqual([30, 30]);
expect(sliderMove(-5, [20, 20], [10, 50], 1, 0)).toEqual([15, 15]);
});
testCase('maxSpan_pull', function (sliderMove) {
expect(sliderMove(-8, [20, 25], [10, 50], 0, null, 4)).toEqual([12, 16]);
expect(sliderMove(14, [20, 25], [10, 50], 0, null, 4)).toEqual([34, 30]);
expect(sliderMove(200, [20, 25], [10, 50], 0, null, 4)).toEqual([50, 46]);
expect(sliderMove(-200, [20, 25], [10, 50], 0, null, 4)).toEqual([10, 14]);
expect(sliderMove(8, [20, 25], [10, 50], 1, null, 4)).toEqual([29, 33]);
expect(sliderMove(-15, [20, 25], [10, 50], 1, null, 4)).toEqual([14, 10]);
expect(sliderMove(-200, [20, 25], [10, 50], 1, null, 4)).toEqual([14, 10]);
expect(sliderMove(200, [20, 25], [10, 50], 1, null, 4)).toEqual([46, 50]);
});
});
});
\ No newline at end of file
......@@ -21,3 +21,4 @@ document.write('<script src="spec/data/List.js"><\/script>');
document.write('<script src="spec/component/visualMap/setOption.js"><\/script>');
document.write('<script src="spec/component/dataZoom/helper.js"><\/script>');
document.write('<script src="spec/component/graphic/setOption.js"><\/script>');
document.write('<script src="spec/component/helper/sliderMove.js"><\/script>');
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册