提交 696227e6 编写于 作者: P pah100

linear brush

上级 9e40ccf5
......@@ -84,18 +84,18 @@ define(function (require) {
var extent = axis.getExtent();
var extra = 30; // Arbitrary value.
var points = [
[extent[0] - extra, -areaWidth / 2],
[extent[0] - extra, areaWidth / 2],
[extent[1] + extra, areaWidth / 2],
[extent[1] + extra, -areaWidth / 2]
];
var rect = {
x: extent[0] - extra,
y: -areaWidth / 2,
width: extent[1] - extent[0] + 2 * extra,
height: areaWidth
};
this._brushController
.mount({container: axisGroup})
.setPanels([{
panelId: 'pl',
points: points
rect: rect
}])
.enableBrush({
brushType: 'lineX',
......
......@@ -34,7 +34,8 @@ define(function(require) {
brushStyle: { // Default brushStyle
// lineWidth: 2,
// stroke: 'rgba(0,0,0,0.3)',
fill: 'rgba(0,0,0,0.15)'
fill: 'rgba(0,0,0,0.15)',
width: null // do not use bursh width in line brush, but fetch from grid.
},
throttleType: 'fixRate',// Throttle in brushSelected event. 'fixRate' or 'debounce'.
......
......@@ -74,7 +74,7 @@ define(function (require) {
_onBrush: function (brushRanges, opt) {
var modelId = this.model.id;
helper.setCoordRanges(brushRanges, this.ecModel);
helper.globalRangesToCoord(brushRanges, this.ecModel);
// Action is not dispatched on drag end, because the drag end
// emits the same params with the last drag move event, and
......@@ -95,7 +95,7 @@ define(function (require) {
(!payload || payload.$from !== brushModel.id) && this._brushController
.setPanels(helper.makePanelOpts(brushModel, ecModel))
.enableBrush(brushModel.brushOption)
.updateCovers(helper.setPanelId(brushModel.brushRanges));
.updateCovers(helper.setPanelIdToRanges(brushModel.brushRanges));
}
});
\ No newline at end of file
......@@ -5,24 +5,35 @@ define(function(require) {
var each = zrUtil.each;
var COORD_NAMES = ['geo', 'grid'];
var PANEL_ID_SPLIT = '---';
var COMPONENT_NAMES = ['geo', 'xAxis', 'yAxis'];
var PANEL_ID_SPLIT = '--';
var COORD_CONVERTS = ['dataToPoint', 'pointToData'];
var helper = {};
helper.convertCoordRanges = function (ecModel) {
helper.coordRangesToGlobal = function (ecModel) {
ecModel.eachComponent({mainType: 'brush'}, function (brushModel) {
each(brushModel.brushRanges, function (brushRange) {
each(COORD_NAMES, function (coordName) {
var coordIndex = brushRange[coordName + 'Index'];
var coordRange = brushRange[coordName + 'Range'];
var coordModel;
if (coordIndex >= 0
&& coordRange
&& (coordModel = ecModel.getComponent(coordName, coordIndex))
) {
var has = false;
each(COMPONENT_NAMES, function (componentName) {
var componentModel = findComponentModel(brushRange, componentName, ecModel);
var componentRange = brushRange[componentName + 'Range'];
if (__DEV__) {
if (componentModel && !componentRange) {
throw new Error(
componentName + 'Range must be specified when ' + componentName + 'Index specified'
);
}
if (has && componentModel) {
throw new Error('Only one coordinateSystem can be specified');
}
}
if (componentModel) {
has = true;
brushRange.range = coordConvert[brushRange.brushType](
'dataToPoint', coordModel.coordinateSystem, coordRange
0, componentModel.coordinateSystem, componentRange, componentModel
);
}
});
......@@ -30,28 +41,44 @@ define(function(require) {
});
};
helper.setCoordRanges = function (brushRanges, ecModel) {
helper.globalRangesToCoord = function (brushRanges, ecModel) {
each(brushRanges, function (brushRange) {
var panelId = brushRange.panelId;
if (panelId) {
panelId = brushRange.panelId.split(PANEL_ID_SPLIT);
var coordName = panelId[0];
var coordIndex = +panelId[1];
var coordModel = ecModel.getComponent(coordName, coordIndex);
brushRange[coordName + 'Index'] = coordIndex;
brushRange[coordName + 'Range'] = coordConvert[brushRange.brushType](
'pointToData', coordModel.coordinateSystem, brushRange.range
var componentName = panelId[0];
var componentIndex = +panelId[1];
var componentModel = ecModel.getComponent(componentName, componentIndex);
brushRange[componentName + 'Index'] = componentIndex;
brushRange[componentName + 'Range'] = coordConvert[brushRange.brushType](
1, componentModel.coordinateSystem, brushRange.range, componentModel
);
}
});
};
helper.setPanelId = function (brushRanges) {
helper.controlSeries = function (brushRange, seriesModel, ecModel) {
// Check whether brushRange in bound in coord, and series do not belong to that coord.
// If do not do this check, some brush (like lineX) will controll all axes.
var isControl = 0;
var hasDefinedCpt;
each(COMPONENT_NAMES, function (componentName) {
var componentIndex = brushRange[componentName + 'Index'];
if (componentIndex >= 0) {
hasDefinedCpt = true;
var componentModel = findComponentModel(brushRange, componentName, ecModel);
isControl |= componentModel.coordinateSystem === seriesModel.coordinateSystem;
}
});
return !hasDefinedCpt || !!isControl;
};
helper.setPanelIdToRanges = function (brushRanges) {
each(brushRanges, function (brushRange) {
each(COORD_NAMES, function (coordName) {
var coordIndex = brushRange[coordName + 'Index'];
if (coordIndex != null) {
brushRange.panelId = coordName + PANEL_ID_SPLIT + coordIndex;
each(COMPONENT_NAMES, function (componentName) {
var componentIndex = brushRange[componentName + 'Index'];
if (componentIndex != null) {
brushRange.panelId = componentName + PANEL_ID_SPLIT + componentIndex;
}
});
});
......@@ -61,38 +88,44 @@ define(function(require) {
helper.makePanelOpts = function (brushModel, ecModel) {
var panelOpts = [];
each(COORD_NAMES, function (coordName) {
var coordIndices = brushModel.option[coordName + 'Index'];
each(COMPONENT_NAMES, function (componentName) {
var componentIndices = brushModel.option[componentName + 'Index'];
if (coordIndices == null) {
if (componentIndices == null) {
return;
}
!zrUtil.isArray(coordIndices) && (coordIndices = [coordIndices]);
if (componentIndices !== 'all' && !zrUtil.isArray(componentIndices)) {
componentIndices = [componentIndices];
}
zrUtil.each(coordIndices, function (coordIndex) {
var coordModel = ecModel.getComponent(coordName, coordIndex);
if (!coordModel) {
ecModel.eachComponent({mainType: componentName}, function (componentModel, index) {
if (componentIndices !== 'all' && zrUtil.indexOf(componentIndices, index) < 0) {
return;
}
var coordSys = coordModel.coordinateSystem;
// FIXME
var coordSys = componentModel.coordinateSystem;
var rect;
// Enumerate different coord.
if (componentName === 'geo') {
// geo is getBoundingRect, grid is getRect.
var r = (coordSys.getRect || coordSys.getBoundingRect).call(coordSys);
var points = [
[r.x, r.y],
[r.x, r.y + r.height],
[r.x + r.width, r.y + r.height],
[r.x + r.width, r.y]
];
var coordTransform = graphic.getTransform(coordSys);
points = zrUtil.map(points, function (point) {
return graphic.applyTransform(point, coordTransform);
});
rect = coordSys.getBoundingRect().clone();
// geo roam and zoom transform
rect.applyTransform(graphic.getTransform(coordSys));
}
else if (componentName === 'xAxis' || componentName === 'yAxis') {
rect = coordSys.grid.getRect().clone();
// grid is not Transformable.
}
else {
if (__DEV__) {
throw new Error('Not support: ' + componentName);
}
}
panelOpts.push({
panelId: coordName + PANEL_ID_SPLIT + coordIndex,
points: points
panelId: componentName + PANEL_ID_SPLIT + index,
rect: rect
});
});
});
......@@ -100,31 +133,46 @@ define(function(require) {
return panelOpts;
};
function findComponentModel(brushRange, componentName, ecModel) {
var componentIndex = brushRange[componentName + 'Index'];
return componentIndex >= 0 && ecModel.getComponent(componentName, componentIndex);
}
function formatMinMax(minMax) {
minMax[0] > minMax[1] && minMax.reverse();
return minMax;
}
function axisConvert(to, coordSys, coordRange, componentModel) {
if (__DEV__) {
zrUtil.assert(componentModel.axis, 'line brush is only available in cartesian (grid).');
}
var axis = componentModel.axis;
return formatMinMax(zrUtil.map([0, 1], function (i) {
return to
? axis.coordToData(axis.toLocalCoord(coordRange[i]))
: axis.toGlobalCoord(axis.dataToCoord(coordRange[i]));
}));
}
var coordConvert = {
lineX: function (to, coordSys, coordRange) {
return [
coordSys[to]([coordRange[0], 0]),
coordSys[to]([coordRange[1], 0])
];
},
lineX: axisConvert,
lineY: function (to, coordSys, coordRange) {
return [
coordSys[to]([0, coordRange[0]]),
coordSys[to]([0, coordRange[1]])
];
},
lineY: axisConvert,
rect: function (to, coordSys, coordRange) {
var xminymin = coordSys[to]([coordRange[0][0], coordRange[1][0]]);
var xmaxymax = coordSys[to]([coordRange[0][1], coordRange[1][1]]);
return [[xminymin[0], xmaxymax[0]], [xminymin[1], xmaxymax[1]]];
var xminymin = coordSys[COORD_CONVERTS[to]]([coordRange[0][0], coordRange[1][0]]);
var xmaxymax = coordSys[COORD_CONVERTS[to]]([coordRange[0][1], coordRange[1][1]]);
return [
formatMinMax([xminymin[0], xmaxymax[0]]),
formatMinMax([xminymin[1], xmaxymax[1]])
];
},
polygon: function (to, coordSys, coordRange) {
return zrUtil.map(coordRange, coordSys[to], coordSys);
return zrUtil.map(coordRange, coordSys[COORD_CONVERTS[to]], coordSys);
}
};
......
......@@ -24,9 +24,25 @@ define(function(require) {
option.toolbox = [toolbox];
}
(toolbox.feature || (toolbox.feature = {})).brush = {type: btns};
var toolboxFeature = (toolbox.feature || (toolbox.feature = {}));
var toolboxBrush = toolboxFeature.brush || (toolboxFeature.brush = {});
var brushTypes = toolboxBrush.type || (toolboxBrush.type = []);
brushTypes.push.apply(brushTypes, btns);
removeDuplicate(brushTypes);
};
function removeDuplicate(arr) {
var map = {};
zrUtil.each(arr, function (val) {
map[val] = 1;
});
arr.length = 0;
zrUtil.each(map, function (flag, val) {
arr.push(val);
});
}
function findToolboxNeededByBrush(option) {
var brush = option && option.brush;
var btns = [];
......
......@@ -13,6 +13,8 @@ define(function(require) {
// function return:
// {boolean} Whether in the given brush.
var selector = {
lineX: getLineSelectors(0),
lineY: getLineSelectors(1),
rect: {
point: function (itemLayout, selectors, brushRange) {
return brushRange.boundingRect.contain(itemLayout[0], itemLayout[1]);
......@@ -57,6 +59,28 @@ define(function(require) {
}
};
function getLineSelectors(xyIndex) {
var xy = ['x', 'y'];
var wh = ['width', 'height'];
return {
point: function (itemLayout, selectors, brushRange) {
var range = brushRange.range;
var p = itemLayout[xyIndex];
return inLineRange(p, range);
},
rect: function (itemLayout, selectors, brushRange) {
var range = brushRange.range;
return inLineRange(itemLayout[xy[xyIndex]], range)
|| inLineRange(itemLayout[xy[xyIndex]] + itemLayout[wh[xyIndex]], range);
}
};
}
function inLineRange(p, range) {
return range[0] <= p && p <= range[1];
}
// FIXME
// 随意写的,没考察过效率。
function lineIntersectPolygon(lx, ly, l2x, l2y, points) {
......
......@@ -20,7 +20,7 @@ define(function (require) {
* Layout for visual, the priority higher than other layout, and before brush visual.
*/
echarts.registerLayout(PRIORITY_BRUSH, function (ecModel, api, payload) {
helper.convertCoordRanges(ecModel);
helper.coordRangesToGlobal(ecModel);
});
/**
......@@ -34,26 +34,29 @@ define(function (require) {
ecModel.eachComponent({mainType: 'brush'}, function (brushModel, brushIndex) {
var thisBrushSelected = {
brushId: brushModel.id,
brushIndex: brushIndex,
brushName: brushModel.name,
brushRanges: zrUtil.clone(brushModel.brushRanges),
series: []
};
// Every brush component exists in event params, convenient
// for user to find by index.
brushSelected.push(thisBrushSelected);
var brushOption = brushModel.option;
var brushLink = brushOption.brushLink;
var linkedSeriesMap = [];
var selectedDataIndexForLink = [];
var rangeInfoBySeries = [];
var supportBrush = [];
if (!brushIndex) { // Only the first throttle setting works.
throttleType = brushOption.throttleType;
throttleDelay = brushOption.throttleDelay;
}
var thisBrushSelected = {
brushId: brushModel.id,
brushIndex: brushIndex,
brushName: brushModel.name,
brushRanges: zrUtil.clone(brushModel.brushRanges),
series: []
};
brushSelected.push(thisBrushSelected);
// Add boundingRect and selectors to range.
var brushRanges = zrUtil.map(brushModel.brushRanges, function (brushRange) {
return bindSelector(
......@@ -78,20 +81,30 @@ define(function (require) {
return brushLink === 'all' || linkedSeriesMap[seriesIndex];
}
// If no supported brush or no brush on the series,
// all visuals should be in original state.
function brushed(rangeInfoList) {
return !!rangeInfoList.length;
}
ecModel.eachSeries(function (seriesModel, seriesIndex) {
var selectorsByBrushType = getSelectorsByBrushType(seriesModel);
if (!selectorsByBrushType || isNotControlledSeries(brushModel, seriesIndex)) {
if (!selectorsByBrushType || brushModelNotControll(brushModel, seriesIndex)) {
return;
}
// Must after selectorsByBrushType judgement.
var rangeInfoList = rangeInfoBySeries[seriesIndex] = [];
zrUtil.each(brushRanges, function (brushRange) {
selectorsByBrushType[brushRange.brushType] && rangeInfoList.push(brushRange);
selectorsByBrushType[brushRange.brushType]
&& (supportBrush[seriesIndex] = true)
&& helper.controlSeries(brushRange, seriesModel, ecModel)
&& rangeInfoList.push(brushRange);
});
if (useLink(seriesIndex)) {
var data = seriesModel.getData();
rangeInfoList.length && data.each(function (dataIndex) {
brushed(rangeInfoList) && data.each(function (dataIndex) {
if (checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex)) {
selectedDataIndexForLink[dataIndex] = 1;
}
......@@ -100,12 +113,24 @@ define(function (require) {
});
ecModel.eachSeries(function (seriesModel, seriesIndex) {
var seriesBrushSelected = {
seriesId: seriesModel.id,
seriesIndex: seriesIndex,
seriesName: seriesModel.name,
rawIndices: []
};
// Every series exists in event params, convenient
// for user to find series by seriesIndex.
thisBrushSelected.series.push(seriesBrushSelected);
var selectorsByBrushType = getSelectorsByBrushType(seriesModel);
if (!selectorsByBrushType || isNotControlledSeries(brushModel, seriesIndex)) {
return;
var rangeInfoList = rangeInfoBySeries[seriesIndex];
if (!rangeInfoList) {
return; // When empty arr, do not return, because brushLink.
}
var data = seriesModel.getData();
var rangeInfoList = rangeInfoBySeries[seriesIndex];
var getValueState = useLink(seriesIndex)
? function (dataIndex) {
return selectedDataIndexForLink[dataIndex]
......@@ -118,18 +143,11 @@ define(function (require) {
: 'outOfBrush';
};
var seriesBrushSelected = {
seriesId: seriesModel.id,
seriesIndex: seriesIndex,
seriesName: seriesModel.name,
rawIndices: []
};
thisBrushSelected.series.push(seriesBrushSelected);
// If no supported brush, all visuals are in original state.
rangeInfoList.length && visualSolution.applyVisual(
STATE_LIST, visualMappings, data, getValueState
);
// If no supported brush or no brush, all visuals are in original state.
(brushed(rangeInfoList) || (useLink(seriesIndex) && supportBrush[seriesIndex]))
&& visualSolution.applyVisual(
STATE_LIST, visualMappings, data, getValueState
);
});
});
......@@ -172,7 +190,10 @@ define(function (require) {
if (!api.isDisposed()) {
var zr = api.getZr();
zr[DISPATCH_FLAG] = true;
api.dispatchAction({type: 'brushSelect', brushComponents: brushSelected});
api.dispatchAction({
type: 'brushSelect',
brushComponents: brushSelected
});
zr[DISPATCH_FLAG] = false;
}
}
......@@ -208,7 +229,7 @@ define(function (require) {
return brushSelector;
}
function isNotControlledSeries(brushModel, seriesIndex) {
function brushModelNotControll(brushModel, seriesIndex) {
var seriesIndices = brushModel.option.seriesIndex;
return seriesIndices != null
&& seriesIndices !== 'all'
......
......@@ -26,8 +26,8 @@ define(function(require) {
zlevel: 0,
z: 4, // Higher than normal component (z: 2).
orient: null, // Default auto by axisIndex. Possible value: 'horizontal', 'vertical'.
xAxisIndex: null, // Default all horizontal category axis.
yAxisIndex: null, // Default all vertical category axis.
xAxisIndex: null, // Default the first horizontal category axis.
yAxisIndex: null, // Default the first vertical category axis.
angleAxisIndex: null,
radiusAxisIndex: null,
filterMode: 'filter', // Possible values: 'filter' or 'empty'.
......
......@@ -138,6 +138,13 @@ define(function (require) {
*/
this._creatingCover;
/**
* true means global panel
* @private
* @type {module:zrender/container/Group|boolean}
*/
this._creatingPanel;
// FIXME
this._useGlobalCursor;
......@@ -197,7 +204,7 @@ define(function (require) {
/**
* @param {Array.<Object>} panelOpts If not pass, it is global brush.
* Each items: {panelId, points}
* Each items: {panelId, rect}
*/
setPanels: function (panelOpts) {
var oldPanels = this._panels || {};
......@@ -208,7 +215,7 @@ define(function (require) {
var panelId = panelOpt.panelId;
var panel = oldPanels[panelId];
if (!panel) {
panel = new graphic.Polygon({
panel = new graphic.Rect({
// FIXME
// 这样靠谱么?
silent: true,
......@@ -226,7 +233,7 @@ define(function (require) {
}
// FIXME
// only support rect panel now.
panel.attr('shape', {points: panelOpt.points});
panel.attr('shape', panelOpt.rect);
panel.__brushPanelId = panelId;
newPanels[panelId] = panel;
oldPanels[panelId] = null;
......@@ -438,16 +445,28 @@ define(function (require) {
}
function getPanelByPoint(controller, x, y) {
if (isGlobalBrush(controller)) {
return {};
var panels = controller._panels;
if (!panels) {
return true; // Global panel
}
var panel;
each(controller._panels, function (pn) {
each(panels, function (pn) {
pn.contain(x, y) && (panel = pn);
});
return panel;
}
function getPanelByCover(controller, cover) {
var panels = controller._panels;
if (!panels) {
return true; // Global panel
}
var panelId = cover.__brushOption.panelId;
// User may give cover without coord sys info,
// which is then treated as global panel.
return panelId != null ? panels[panelId] : true;
}
function clearCovers(controller) {
each(controller._covers, function (cover) {
controller.group.remove(cover);
......@@ -505,10 +524,6 @@ define(function (require) {
}
}
function isGlobalBrush(controller) {
return !controller._panels;
}
function getTrackEnds(track) {
var tail = track.length - 1;
tail < 0 && (tail = 0);
......@@ -546,7 +561,7 @@ define(function (require) {
return cover;
}
function updateBaseRect(cover, localRange, brushOption) {
function updateBaseRect(controller, cover, localRange, brushOption) {
var lineWidth = brushOption.brushStyle.lineWidth || 0;
var handleSize = mathMax(lineWidth, MIN_RESIZE_LINE_WIDTH);
var x = localRange[0][0];
......@@ -562,18 +577,18 @@ define(function (require) {
var widtha = width + lineWidth;
var heighta = height + lineWidth;
updateRectShape(cover, 'rect', x, y, width, height);
updateRectShape(controller, cover, 'rect', x, y, width, height);
if (brushOption.transformable) {
updateRectShape(cover, 'w', xa, ya, handleSize, heighta);
updateRectShape(cover, 'e', x2a, ya, handleSize, heighta);
updateRectShape(cover, 'n', xa, ya, widtha, handleSize);
updateRectShape(cover, 's', xa, y2a, widtha, handleSize);
updateRectShape(cover, 'nw', xa, ya, handleSize, handleSize);
updateRectShape(cover, 'ne', x2a, ya, handleSize, handleSize);
updateRectShape(cover, 'sw', xa, y2a, handleSize, handleSize);
updateRectShape(cover, 'se', x2a, y2a, handleSize, handleSize);
updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta);
updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta);
updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize);
updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize);
updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize);
updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize);
updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize);
updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize);
}
}
......@@ -603,9 +618,11 @@ define(function (require) {
);
}
function updateRectShape(cover, name, x, y, w, h) {
function updateRectShape(controller, cover, name, x, y, w, h) {
var el = cover.childOfName(name);
el && el.setShape({x: x, y: y, width: w, height: h});
el && el.setShape(pointsToRect(
clipByPanel(controller, cover, [[x, y], [x + w, y + h]])
));
}
function makeStyle(brushOption) {
......@@ -646,7 +663,11 @@ define(function (require) {
}
}
function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy) {
function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy, e) {
if (!isInPanel(controller, cover, e)) {
return;
}
var brushOption = cover.__brushOption;
var rectRange = toRectRange(brushOption.range);
var localDelta = toLocalDelta(controller, dx, dy);
......@@ -664,7 +685,11 @@ define(function (require) {
trigger(controller, {isEnd: false});
}
function driftPolygon(controller, cover, dx, dy) {
function driftPolygon(controller, cover, dx, dy, e) {
if (!isInPanel(controller, cover, e)) {
return;
}
var range = cover.__brushOption.range;
var localDelta = toLocalDelta(controller, dx, dy);
......@@ -677,6 +702,14 @@ define(function (require) {
trigger(controller, {isEnd: false});
}
function isInPanel(controller, cover, e) {
var panel = getPanelByCover(controller, cover);
var x = e.offsetX;
var y = e.offsetY;
return panel === true || panel.contain(x, y);
}
function toLocalDelta(controller, dx, dy) {
var thisGroup = controller.group;
var localD = thisGroup.transformCoordToLocal(dx, dy);
......@@ -685,6 +718,39 @@ define(function (require) {
return [localD[0] - localZero[0], localD[1] - localZero[1]];
}
function clipByPanel(controller, cover, data) {
var panel = getPanelByCover(controller, cover);
if (panel === true) { // Global panel
return zrUtil.clone(data);
}
var panelRect = panel.getBoundingRect();
return zrUtil.map(data, function (point) {
var x = point[0];
x = mathMax(x, panelRect.x);
x = mathMin(x, panelRect.x + panelRect.width);
var y = point[1];
y = mathMax(y, panelRect.y);
y = mathMin(y, panelRect.y + panelRect.height);
return [x, y];
});
}
function pointsToRect(points) {
var xmin = mathMin(points[0][0], points[1][0]);
var ymin = mathMin(points[0][1], points[1][1]);
var xmax = mathMax(points[0][0], points[1][0]);
var ymax = mathMax(points[0][1], points[1][1]);
return {
x: xmin,
y: ymin,
width: xmax - xmin,
height: ymax - ymin
};
}
function preventDefault(e) {
var rawE = e.event;
rawE.preventDefault && rawE.preventDefault();
......@@ -694,55 +760,51 @@ define(function (require) {
var x = e.offsetX;
var y = e.offsetY;
var creatingCover = controller._creatingCover;
var panel = controller._creatingPanel;
var thisBrushOption = controller._brushOption;
var panel = getPanelByPoint(controller, x, y);
if (panel || isEnd) { // Outside panel but isEnd, cover creating ends.
panel && controller._track.push(controller.group.transformCoordToLocal(x, y));
controller._track.push(controller.group.transformCoordToLocal(x, y));
if (shouldShowCover(controller)) {
if (shouldShowCover(controller)) {
if (!creatingCover && panel) {
thisBrushOption.brushMode === 'single' && clearCovers(controller);
var brushOption = zrUtil.clone(thisBrushOption);
brushOption.panelId = panel.__brushPanelId;
creatingCover = controller._creatingCover = createCover(controller, brushOption);
controller._covers.push(creatingCover);
}
if (creatingCover) {
var coverRenderer = coverRenderers[controller._brushType];
var coverBrushOption = creatingCover.__brushOption;
if (panel && !creatingCover) {
thisBrushOption.brushMode === 'single' && clearCovers(controller);
var brushOption = zrUtil.clone(thisBrushOption);
brushOption.panelId = panel === true ? null : panel.__brushPanelId;
creatingCover = controller._creatingCover = createCover(controller, brushOption);
controller._covers.push(creatingCover);
}
coverBrushOption.range = coverRenderer.getCreatingRange(
zrUtil.clone(controller._track)
);
if (creatingCover) {
var coverRenderer = coverRenderers[controller._brushType];
var coverBrushOption = creatingCover.__brushOption;
if (isEnd) {
endCreating(controller, creatingCover);
coverRenderer.updateCommon(controller, creatingCover);
}
coverBrushOption.range = coverRenderer.getCreatingRange(
clipByPanel(controller, creatingCover, controller._track)
);
updateCoverShape(controller, creatingCover);
if (isEnd) {
endCreating(controller, creatingCover);
coverRenderer.updateCommon(controller, creatingCover);
}
updateCoverShape(controller, creatingCover);
trigger(controller, {isEnd: isEnd});
}
else if (
isEnd
&& !creatingCover
&& thisBrushOption.brushMode === 'single'
&& thisBrushOption.removeOnClick
) {
// Help user to remove covers easily, only by a tiny drag, in 'single' mode.
// But a single click do not clear covers, because user may have casual
// clicks (for example, click on other component and do not expect covers
// disappear).
clearCovers(controller);
trigger(controller, {isEnd: isEnd, removeOnClick: true});
}
}
else if (
isEnd
&& !creatingCover
&& thisBrushOption.brushMode === 'single'
&& thisBrushOption.removeOnClick
) {
// Help user to remove covers easily, only by a tiny drag, in 'single' mode.
// But a single click do not clear covers, because user may have casual
// clicks (for example, click on other component and do not expect covers
// disappear).
clearCovers(controller);
trigger(controller, {isEnd: isEnd, removeOnClick: true});
}
}
......@@ -757,8 +819,9 @@ define(function (require) {
var y = e.offsetY;
this._creatingCover = null;
var panel = this._creatingPanel = getPanelByPoint(this, x, y);
if (getPanelByPoint(this, x, y)) {
if (panel) {
this._dragging = true;
this._track = [this.group.transformCoordToLocal(x, y)];
}
......@@ -819,7 +882,7 @@ define(function (require) {
return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]);
},
updateCoverShape: function (controller, cover, localRange, brushOption) {
updateBaseRect(cover, localRange, brushOption);
updateBaseRect(controller, cover, localRange, brushOption);
},
updateCommon: updateCommon
},
......@@ -850,7 +913,9 @@ define(function (require) {
}));
},
updateCoverShape: function (controller, cover, localRange, brushOption) {
cover.childAt(0).setShape({points: localRange});
cover.childAt(0).setShape({
points: clipByPanel(controller, cover, localRange)
});
},
updateCommon: updateCommon
}
......@@ -878,17 +943,34 @@ define(function (require) {
},
getCreatingRange: function (localTrack) {
var ends = getTrackEnds(localTrack);
var min = mathMin(ends[0][0], ends[1 - xyIndex][xyIndex]);
var max = mathMax(ends[0][0], ends[1 - xyIndex][xyIndex]);
var min = mathMin(ends[0][xyIndex], ends[1][xyIndex]);
var max = mathMax(ends[0][xyIndex], ends[1][xyIndex]);
return [min, max];
},
updateCoverShape: function (controller, cover, localRange, brushOption) {
var t = brushOption.brushStyle.width;
var rectRange = [localRange, [-t / 2, t / 2]];
var brushWidth = brushOption.brushStyle.width;
var otherExtent;
// If brushWidth not specified, fit the panel.
if (brushWidth == null) {
var panel = getPanelByCover(controller, cover);
var base = 0;
if (panel !== true) {
var rect = panel.getBoundingRect();
brushWidth = xyIndex ? rect.width : rect.height;
base = xyIndex ? rect.x : rect.y;
}
// FIXME
// do not support global panel yet.
otherExtent = [base, base + (brushWidth || 0)];
}
else {
otherExtent = [-brushWidth / 2, brushWidth / 2];
}
var rectRange = [localRange, otherExtent];
xyIndex && rectRange.reverse();
updateBaseRect(cover, rectRange, brushOption);
updateBaseRect(controller, cover, rectRange, brushOption);
},
updateCommon: updateCommon
};
......
......@@ -4,6 +4,8 @@ define(function(require) {
var featureManager = require('../featureManager');
var zrUtil = require('zrender/core/util');
require('../../brush');
function Brush(model, ecModel, api) {
this.model = model;
this.ecModel = ecModel;
......@@ -28,12 +30,16 @@ define(function(require) {
icon: {
rect: 'M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13',
polygon: 'M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2',
lineX: 'M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2',
lineY: 'M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2',
keep: 'M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z',
clear: 'M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2'
},
title: {
rect: '矩形选择',
polygon: '圈选',
lineX: '横向选择',
lineY: '纵向选择',
keep: '保持选择',
clear: '清除选择'
}
......
......@@ -250,6 +250,8 @@ define(function(require, factory) {
cartesian.addAxis(xAxis);
cartesian.addAxis(yAxis);
xAxis.model.coordinateSystem = yAxis.model.coordinateSystem = cartesian;
}, this);
}, this);
......
......@@ -24,7 +24,7 @@
}
body .main {
height: 550px;
margin-right: 300px;
margin-right: 220px;
margin-left: 20px;
}
.panel {
......@@ -32,7 +32,7 @@
top: 0;
right: 0;
bottom: 0;
width: 280px;
width: 200px;
background: #555;
color: #fff;
font-size: 14px;
......@@ -94,7 +94,7 @@
<li>+++++++++++.</li>
<li><strong>CHECK</strong>: brush an area and drag dataZoom.</li>
</ul>
<div id="panel2"></div>
<div id="panel1"></div>
</div>
</div>
......@@ -113,12 +113,14 @@
*/
var chart;
var data;
var panel;
require([
'echarts',
'data/security-sh-2013.json',
'data/stock-DJI.json',
'echarts/chart/candlestick',
'echarts/chart/line',
'echarts/chart/bar',
'echarts/component/title',
'echarts/component/legend',
'echarts/component/grid',
......@@ -133,53 +135,66 @@
chart = echarts.init(document.getElementById('main1'), null, {
renderer: 'canvas'
});
panel = document.getElementById('panel1');
data = splitData(rawData);
update();
chart.on('click', function (info) {
console.log(info);
if (info.data && info.data.length === 4) {
if (info.data && info.componentType === 'series') {
alert([
'clicked on: ',
'DATA: ' + info.name,
'OPEN: ' + info.data[0],
'CLOSE: ' + info.data[1],
'LOWEST: ' + info.data[2],
'HIGHEST: ' + info.data[3]
'HIGHEST: ' + info.data[3],
'VOLUMN: ' + info.data[4]
].join('\n'));
}
else if (info.data && info.data.length === 2) {
// Markpoint
alert('mark point');
}
});
})
function splitData(rawData) {
var categoryData = [];
var values = []
var values = [];
var volumns = [];
for (var i = 0; i < rawData.length; i++) {
categoryData.push(rawData[i].splice(0, 1)[0]);
values.push(rawData[i])
volumns.push(rawData[i][4]);
}
return {
categoryData: categoryData,
values: values
values: values,
volumns: volumns
};
}
function parseDate(timestamp) {
var date = new Date(timestamp);
return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
function calculateMA(dayCount, data) {
var result = [];
for (var i = 0, len = data.values.length; i < len; i++) {
if (i < dayCount) {
result.push('-');
continue;
}
var sum = 0;
for (var j = 0; j < dayCount; j++) {
sum += data.values[i - j][1];
}
result.push(+(sum / dayCount).toFixed(3));
}
return result;
}
function update() {
chart.setOption({
backgroundColor: '#eee',
animation: false,
legend: {
data: ['上证指数', '开盘']
left: 0,
data: ['Dow-Jones index', 'MA5', 'MA10', 'MA20', 'MA30']
},
tooltip: {
trigger: 'axis',
......@@ -187,79 +202,188 @@
type: 'line'
}
},
brush: {
brushRanges: [
{brushType: 'rect', range: [[200, 300], [100, 350]]}
]
},
grid: {
left: '10%',
right: '10%',
bottom: '15%'
toolbox: {
feature: {
dataZoom: {},
brush: {
type: ['polygon', 'rect', 'lineX', 'lineY', 'keep', 'clear']
}
}
},
xAxis: {
type: 'category',
data: data.categoryData,
scale: true,
boundaryGap : false,
axisLine: {onZero: false},
splitLine: {show: false},
splitNumber: 20,
min: 'dataMin',
max: 'dataMax'
brush: {
xAxisIndex: 'all',
brushLink: 'all'
},
yAxis: {
scale: true,
splitArea: {
show: true
grid: [
{
left: '10%',
right: '10%',
height: 300
},
{
left: '10%',
right: '10%',
height: 70,
bottom: 80
}
},
],
xAxis: [
{
type: 'category',
data: data.categoryData,
scale: true,
boundaryGap : false,
axisLine: {onZero: false},
splitLine: {show: false},
splitNumber: 20,
min: 'dataMin',
max: 'dataMax'
},
{
type: 'category',
gridIndex: 1,
data: data.categoryData,
scale: true,
boundaryGap : false,
axisLine: {onZero: false},
axisTick: {show: false},
splitLine: {show: false},
axisLabel: {show: false},
splitNumber: 20,
min: 'dataMin',
max: 'dataMax'
}
],
yAxis: [
{
scale: true,
splitArea: {
show: true
}
},
{
scale: true,
gridIndex: 1,
splitNumber: 2,
axisLabel: {show: false},
axisLine: {show: false},
axisTick: {show: false},
splitLine: {show: false}
}
],
dataZoom: [
{
type: 'inside',
start: 50,
xAxisIndex: [0, 1],
start: 98,
end: 100
},
{
show: true,
xAxisIndex: [0, 1],
type: 'slider',
y: '90%',
start: 50,
bottom: 10,
start: 98,
end: 100
}
],
series: [
{
name: '开盘',
type: 'line',
data: (function () {
opens = [];
for (var i = 0, len = data.values.length; i < len; i++) {
opens.push(data.values[i][0]);
}
return opens;
})(),
smooth: true,
lineStyle: {
normal: {color: '#aaa'}
}
},
{
name: '上证指数',
name: 'Dow-Jones index',
type: 'candlestick',
data: data.values,
tooltip: {
formatter: function (param) {
var param = param[0];
return [
'日期:' + param.name + '<hr size=1 style="margin: 3px 0">',
'开盘:' + param.data[0] + '<br/>',
'收盘:' + param.data[1] + '<br/>',
'日最低:' + param.data[2] + '<br/>',
'日最高:' + param.data[3] + '<br/>'
'Date: ' + param.name + '<hr size=1 style="margin: 3px 0">',
'Open: ' + param.data[0] + '<br/>',
'Close: ' + param.data[1] + '<br/>',
'Lowest: ' + param.data[2] + '<br/>',
'Highest: ' + param.data[3] + '<br/>'
].join('')
}
}
},
{
name: 'MA5',
type: 'line',
data: calculateMA(5, data),
smooth: true,
lineStyle: {
normal: {opacity: 0.5}
}
},
{
name: 'MA10',
type: 'line',
data: calculateMA(10, data),
smooth: true,
lineStyle: {
normal: {opacity: 0.5}
}
},
{
name: 'MA20',
type: 'line',
data: calculateMA(20, data),
smooth: true,
lineStyle: {
normal: {opacity: 0.5}
}
},
{
name: 'MA30',
type: 'line',
data: calculateMA(30, data),
smooth: true,
lineStyle: {
normal: {opacity: 0.5}
}
},
{
name: 'Volumn',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 1,
data: data.volumns
}
]
});
chart.on('brushSelected', renderBrushed);
function renderBrushed(params) {
var sum = 0;
var min = Infinity;
var max = -Infinity;
var countBySeries = [];
var brushComponent = params.brushComponents[0];
var rawIndices = brushComponent.series[0].rawIndices;
for (var i = 0; i < rawIndices.length; i++) {
var val = data.values[rawIndices[i]][1];
sum += val;
min = Math.min(val, min);
max = Math.max(val, max);
}
panel.innerHTML = [
'<h3>STATISTICS:</h3>',
'SUM of open: ' + (sum / rawIndices.length).toFixed(4) + '<br>',
'MIN of open: ' + min.toFixed(4) + '<br>',
'MAX of open: ' + max.toFixed(4) + '<br>'
].join(' ');
}
chart.dispatchAction({
type: 'brush',
brushRanges: [
{
brushType: 'lineX',
xAxisRange: ['2016-06-02', '2016-06-20'],
xAxisIndex: 0
}
]
});
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册