| /* |
| * 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. |
| */ |
| import { __DEV__ } from '../../config'; |
| import * as zrUtil from 'zrender/src/core/util'; |
| import Eventful from 'zrender/src/mixin/Eventful'; |
| import * as graphic from '../../util/graphic'; |
| import * as interactionMutex from './interactionMutex'; |
| import DataDiffer from '../../data/DataDiffer'; |
| var curry = zrUtil.curry; |
| var each = zrUtil.each; |
| var map = zrUtil.map; |
| var mathMin = Math.min; |
| var mathMax = Math.max; |
| var mathPow = Math.pow; |
| var COVER_Z = 10000; |
| var UNSELECT_THRESHOLD = 6; |
| var MIN_RESIZE_LINE_WIDTH = 6; |
| var MUTEX_RESOURCE_KEY = 'globalPan'; |
| var DIRECTION_MAP = { |
| w: [0, 0], |
| e: [0, 1], |
| n: [1, 0], |
| s: [1, 1] |
| }; |
| var CURSOR_MAP = { |
| w: 'ew', |
| e: 'ew', |
| n: 'ns', |
| s: 'ns', |
| ne: 'nesw', |
| sw: 'nesw', |
| nw: 'nwse', |
| se: 'nwse' |
| }; |
| var DEFAULT_BRUSH_OPT = { |
| brushStyle: { |
| lineWidth: 2, |
| stroke: 'rgba(0,0,0,0.3)', |
| fill: 'rgba(0,0,0,0.1)' |
| }, |
| transformable: true, |
| brushMode: 'single', |
| removeOnClick: false |
| }; |
| var baseUID = 0; |
| /** |
| * @alias module:echarts/component/helper/BrushController |
| * @constructor |
| * @mixin {module:zrender/mixin/Eventful} |
| * @event module:echarts/component/helper/BrushController#brush |
| * params: |
| * areas: Array.<Array>, coord relates to container group, |
| * If no container specified, to global. |
| * opt { |
| * isEnd: boolean, |
| * removeOnClick: boolean |
| * } |
| * |
| * @param {module:zrender/zrender~ZRender} zr |
| */ |
| |
| function BrushController(zr) { |
| Eventful.call(this); |
| /** |
| * @type {module:zrender/zrender~ZRender} |
| * @private |
| */ |
| |
| this._zr = zr; |
| /** |
| * @type {module:zrender/container/Group} |
| * @readOnly |
| */ |
| |
| this.group = new graphic.Group(); |
| /** |
| * Only for drawing (after enabledBrush). |
| * 'line', 'rect', 'polygon' or false |
| * If passing false/null/undefined, disable brush. |
| * If passing 'auto', determined by panel.defaultBrushType |
| * @private |
| * @type {string} |
| */ |
| |
| this._brushType; |
| /** |
| * Only for drawing (after enabledBrush). |
| * |
| * @private |
| * @type {Object} |
| */ |
| |
| this._brushOption; |
| /** |
| * @private |
| * @type {Object} |
| */ |
| |
| this._panels; |
| /** |
| * @private |
| * @type {Array.<nubmer>} |
| */ |
| |
| this._track = []; |
| /** |
| * @private |
| * @type {boolean} |
| */ |
| |
| this._dragging; |
| /** |
| * @private |
| * @type {Array} |
| */ |
| |
| this._covers = []; |
| /** |
| * @private |
| * @type {moudule:zrender/container/Group} |
| */ |
| |
| this._creatingCover; |
| /** |
| * `true` means global panel |
| * @private |
| * @type {module:zrender/container/Group|boolean} |
| */ |
| |
| this._creatingPanel; |
| /** |
| * @private |
| * @type {boolean} |
| */ |
| |
| this._enableGlobalPan; |
| /** |
| * @private |
| * @type {boolean} |
| */ |
| |
| /** |
| * @private |
| * @type {string} |
| */ |
| this._uid = 'brushController_' + baseUID++; |
| /** |
| * @private |
| * @type {Object} |
| */ |
| |
| this._handlers = {}; |
| each(pointerHandlers, function (handler, eventName) { |
| this._handlers[eventName] = zrUtil.bind(handler, this); |
| }, this); |
| } |
| |
| BrushController.prototype = { |
| constructor: BrushController, |
| |
| /** |
| * If set to null/undefined/false, select disabled. |
| * @param {Object} brushOption |
| * @param {string|boolean} brushOption.brushType 'line', 'rect', 'polygon' or false |
| * If passing false/null/undefined, disable brush. |
| * If passing 'auto', determined by panel.defaultBrushType. |
| * ('auto' can not be used in global panel) |
| * @param {number} [brushOption.brushMode='single'] 'single' or 'multiple' |
| * @param {boolean} [brushOption.transformable=true] |
| * @param {boolean} [brushOption.removeOnClick=false] |
| * @param {Object} [brushOption.brushStyle] |
| * @param {number} [brushOption.brushStyle.width] |
| * @param {number} [brushOption.brushStyle.lineWidth] |
| * @param {string} [brushOption.brushStyle.stroke] |
| * @param {string} [brushOption.brushStyle.fill] |
| * @param {number} [brushOption.z] |
| */ |
| enableBrush: function (brushOption) { |
| this._brushType && doDisableBrush(this); |
| brushOption.brushType && doEnableBrush(this, brushOption); |
| return this; |
| }, |
| |
| /** |
| * @param {Array.<Object>} panelOpts If not pass, it is global brush. |
| * Each items: { |
| * panelId, // mandatory. |
| * clipPath, // mandatory. function. |
| * isTargetByCursor, // mandatory. function. |
| * defaultBrushType, // optional, only used when brushType is 'auto'. |
| * getLinearBrushOtherExtent, // optional. function. |
| * } |
| */ |
| setPanels: function (panelOpts) { |
| if (panelOpts && panelOpts.length) { |
| var panels = this._panels = {}; |
| zrUtil.each(panelOpts, function (panelOpts) { |
| panels[panelOpts.panelId] = zrUtil.clone(panelOpts); |
| }); |
| } else { |
| this._panels = null; |
| } |
| |
| return this; |
| }, |
| |
| /** |
| * @param {Object} [opt] |
| * @return {boolean} [opt.enableGlobalPan=false] |
| */ |
| mount: function (opt) { |
| opt = opt || {}; |
| this._enableGlobalPan = opt.enableGlobalPan; |
| var thisGroup = this.group; |
| |
| this._zr.add(thisGroup); |
| |
| thisGroup.attr({ |
| position: opt.position || [0, 0], |
| rotation: opt.rotation || 0, |
| scale: opt.scale || [1, 1] |
| }); |
| this._transform = thisGroup.getLocalTransform(); |
| return this; |
| }, |
| eachCover: function (cb, context) { |
| each(this._covers, cb, context); |
| }, |
| |
| /** |
| * Update covers. |
| * @param {Array.<Object>} brushOptionList Like: |
| * [ |
| * {id: 'xx', brushType: 'line', range: [23, 44], brushStyle, transformable}, |
| * {id: 'yy', brushType: 'rect', range: [[23, 44], [23, 54]]}, |
| * ... |
| * ] |
| * `brushType` is required in each cover info. (can not be 'auto') |
| * `id` is not mandatory. |
| * `brushStyle`, `transformable` is not mandatory, use DEFAULT_BRUSH_OPT by default. |
| * If brushOptionList is null/undefined, all covers removed. |
| */ |
| updateCovers: function (brushOptionList) { |
| brushOptionList = zrUtil.map(brushOptionList, function (brushOption) { |
| return zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true); |
| }); |
| var tmpIdPrefix = '\0-brush-index-'; |
| var oldCovers = this._covers; |
| var newCovers = this._covers = []; |
| var controller = this; |
| var creatingCover = this._creatingCover; |
| new DataDiffer(oldCovers, brushOptionList, oldGetKey, getKey).add(addOrUpdate).update(addOrUpdate).remove(remove).execute(); |
| return this; |
| |
| function getKey(brushOption, index) { |
| return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index) + '-' + brushOption.brushType; |
| } |
| |
| function oldGetKey(cover, index) { |
| return getKey(cover.__brushOption, index); |
| } |
| |
| function addOrUpdate(newIndex, oldIndex) { |
| var newBrushOption = brushOptionList[newIndex]; // Consider setOption in event listener of brushSelect, |
| // where updating cover when creating should be forbiden. |
| |
| if (oldIndex != null && oldCovers[oldIndex] === creatingCover) { |
| newCovers[newIndex] = oldCovers[oldIndex]; |
| } else { |
| var cover = newCovers[newIndex] = oldIndex != null ? (oldCovers[oldIndex].__brushOption = newBrushOption, oldCovers[oldIndex]) : endCreating(controller, createCover(controller, newBrushOption)); |
| updateCoverAfterCreation(controller, cover); |
| } |
| } |
| |
| function remove(oldIndex) { |
| if (oldCovers[oldIndex] !== creatingCover) { |
| controller.group.remove(oldCovers[oldIndex]); |
| } |
| } |
| }, |
| unmount: function () { |
| this.enableBrush(false); // container may 'removeAll' outside. |
| |
| clearCovers(this); |
| |
| this._zr.remove(this.group); |
| |
| return this; |
| }, |
| dispose: function () { |
| this.unmount(); |
| this.off(); |
| } |
| }; |
| zrUtil.mixin(BrushController, Eventful); |
| |
| function doEnableBrush(controller, brushOption) { |
| var zr = controller._zr; // Consider roam, which takes globalPan too. |
| |
| if (!controller._enableGlobalPan) { |
| interactionMutex.take(zr, MUTEX_RESOURCE_KEY, controller._uid); |
| } |
| |
| mountHandlers(zr, controller._handlers); |
| controller._brushType = brushOption.brushType; |
| controller._brushOption = zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true); |
| } |
| |
| function doDisableBrush(controller) { |
| var zr = controller._zr; |
| interactionMutex.release(zr, MUTEX_RESOURCE_KEY, controller._uid); |
| unmountHandlers(zr, controller._handlers); |
| controller._brushType = controller._brushOption = null; |
| } |
| |
| function mountHandlers(zr, handlers) { |
| each(handlers, function (handler, eventName) { |
| zr.on(eventName, handler); |
| }); |
| } |
| |
| function unmountHandlers(zr, handlers) { |
| each(handlers, function (handler, eventName) { |
| zr.off(eventName, handler); |
| }); |
| } |
| |
| function createCover(controller, brushOption) { |
| var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption); |
| cover.__brushOption = brushOption; |
| updateZ(cover, brushOption); |
| controller.group.add(cover); |
| return cover; |
| } |
| |
| function endCreating(controller, creatingCover) { |
| var coverRenderer = getCoverRenderer(creatingCover); |
| |
| if (coverRenderer.endCreating) { |
| coverRenderer.endCreating(controller, creatingCover); |
| updateZ(creatingCover, creatingCover.__brushOption); |
| } |
| |
| return creatingCover; |
| } |
| |
| function updateCoverShape(controller, cover) { |
| var brushOption = cover.__brushOption; |
| getCoverRenderer(cover).updateCoverShape(controller, cover, brushOption.range, brushOption); |
| } |
| |
| function updateZ(cover, brushOption) { |
| var z = brushOption.z; |
| z == null && (z = COVER_Z); |
| cover.traverse(function (el) { |
| el.z = z; |
| el.z2 = z; // Consider in given container. |
| }); |
| } |
| |
| function updateCoverAfterCreation(controller, cover) { |
| getCoverRenderer(cover).updateCommon(controller, cover); |
| updateCoverShape(controller, cover); |
| } |
| |
| function getCoverRenderer(cover) { |
| return coverRenderers[cover.__brushOption.brushType]; |
| } // return target panel or `true` (means global panel) |
| |
| |
| function getPanelByPoint(controller, e, localCursorPoint) { |
| var panels = controller._panels; |
| |
| if (!panels) { |
| return true; // Global panel |
| } |
| |
| var panel; |
| var transform = controller._transform; |
| each(panels, function (pn) { |
| pn.isTargetByCursor(e, localCursorPoint, transform) && (panel = pn); |
| }); |
| return panel; |
| } // Return a panel or true |
| |
| |
| 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) { |
| var covers = controller._covers; |
| var originalLength = covers.length; |
| each(covers, function (cover) { |
| controller.group.remove(cover); |
| }, controller); |
| covers.length = 0; |
| return !!originalLength; |
| } |
| |
| function trigger(controller, opt) { |
| var areas = map(controller._covers, function (cover) { |
| var brushOption = cover.__brushOption; |
| var range = zrUtil.clone(brushOption.range); |
| return { |
| brushType: brushOption.brushType, |
| panelId: brushOption.panelId, |
| range: range |
| }; |
| }); |
| controller.trigger('brush', areas, { |
| isEnd: !!opt.isEnd, |
| removeOnClick: !!opt.removeOnClick |
| }); |
| } |
| |
| function shouldShowCover(controller) { |
| var track = controller._track; |
| |
| if (!track.length) { |
| return false; |
| } |
| |
| var p2 = track[track.length - 1]; |
| var p1 = track[0]; |
| var dx = p2[0] - p1[0]; |
| var dy = p2[1] - p1[1]; |
| var dist = mathPow(dx * dx + dy * dy, 0.5); |
| return dist > UNSELECT_THRESHOLD; |
| } |
| |
| function getTrackEnds(track) { |
| var tail = track.length - 1; |
| tail < 0 && (tail = 0); |
| return [track[0], track[tail]]; |
| } |
| |
| function createBaseRectCover(doDrift, controller, brushOption, edgeNames) { |
| var cover = new graphic.Group(); |
| cover.add(new graphic.Rect({ |
| name: 'main', |
| style: makeStyle(brushOption), |
| silent: true, |
| draggable: true, |
| cursor: 'move', |
| drift: curry(doDrift, controller, cover, 'nswe'), |
| ondragend: curry(trigger, controller, { |
| isEnd: true |
| }) |
| })); |
| each(edgeNames, function (name) { |
| cover.add(new graphic.Rect({ |
| name: name, |
| style: { |
| opacity: 0 |
| }, |
| draggable: true, |
| silent: true, |
| invisible: true, |
| drift: curry(doDrift, controller, cover, name), |
| ondragend: curry(trigger, controller, { |
| isEnd: true |
| }) |
| })); |
| }); |
| return cover; |
| } |
| |
| 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]; |
| var y = localRange[1][0]; |
| var xa = x - lineWidth / 2; |
| var ya = y - lineWidth / 2; |
| var x2 = localRange[0][1]; |
| var y2 = localRange[1][1]; |
| var x2a = x2 - handleSize + lineWidth / 2; |
| var y2a = y2 - handleSize + lineWidth / 2; |
| var width = x2 - x; |
| var height = y2 - y; |
| var widtha = width + lineWidth; |
| var heighta = height + lineWidth; |
| updateRectShape(controller, cover, 'main', x, y, width, height); |
| |
| if (brushOption.transformable) { |
| 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); |
| } |
| } |
| |
| function updateCommon(controller, cover) { |
| var brushOption = cover.__brushOption; |
| var transformable = brushOption.transformable; |
| var mainEl = cover.childAt(0); |
| mainEl.useStyle(makeStyle(brushOption)); |
| mainEl.attr({ |
| silent: !transformable, |
| cursor: transformable ? 'move' : 'default' |
| }); |
| each(['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'], function (name) { |
| var el = cover.childOfName(name); |
| var globalDir = getGlobalDirection(controller, name); |
| el && el.attr({ |
| silent: !transformable, |
| invisible: !transformable, |
| cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null |
| }); |
| }); |
| } |
| |
| function updateRectShape(controller, cover, name, x, y, w, h) { |
| var el = cover.childOfName(name); |
| el && el.setShape(pointsToRect(clipByPanel(controller, cover, [[x, y], [x + w, y + h]]))); |
| } |
| |
| function makeStyle(brushOption) { |
| return zrUtil.defaults({ |
| strokeNoScale: true |
| }, brushOption.brushStyle); |
| } |
| |
| function formatRectRange(x, y, x2, y2) { |
| var min = [mathMin(x, x2), mathMin(y, y2)]; |
| var max = [mathMax(x, x2), mathMax(y, y2)]; |
| return [[min[0], max[0]], // x range |
| [min[1], max[1]] // y range |
| ]; |
| } |
| |
| function getTransform(controller) { |
| return graphic.getTransform(controller.group); |
| } |
| |
| function getGlobalDirection(controller, localDirection) { |
| if (localDirection.length > 1) { |
| localDirection = localDirection.split(''); |
| var globalDir = [getGlobalDirection(controller, localDirection[0]), getGlobalDirection(controller, localDirection[1])]; |
| (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse(); |
| return globalDir.join(''); |
| } else { |
| var map = { |
| w: 'left', |
| e: 'right', |
| n: 'top', |
| s: 'bottom' |
| }; |
| var inverseMap = { |
| left: 'w', |
| right: 'e', |
| top: 'n', |
| bottom: 's' |
| }; |
| var globalDir = graphic.transformDirection(map[localDirection], getTransform(controller)); |
| return inverseMap[globalDir]; |
| } |
| } |
| |
| function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy, e) { |
| var brushOption = cover.__brushOption; |
| var rectRange = toRectRange(brushOption.range); |
| var localDelta = toLocalDelta(controller, dx, dy); |
| each(name.split(''), function (namePart) { |
| var ind = DIRECTION_MAP[namePart]; |
| rectRange[ind[0]][ind[1]] += localDelta[ind[0]]; |
| }); |
| brushOption.range = fromRectRange(formatRectRange(rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1])); |
| updateCoverAfterCreation(controller, cover); |
| trigger(controller, { |
| isEnd: false |
| }); |
| } |
| |
| function driftPolygon(controller, cover, dx, dy, e) { |
| var range = cover.__brushOption.range; |
| var localDelta = toLocalDelta(controller, dx, dy); |
| each(range, function (point) { |
| point[0] += localDelta[0]; |
| point[1] += localDelta[1]; |
| }); |
| updateCoverAfterCreation(controller, cover); |
| trigger(controller, { |
| isEnd: false |
| }); |
| } |
| |
| function toLocalDelta(controller, dx, dy) { |
| var thisGroup = controller.group; |
| var localD = thisGroup.transformCoordToLocal(dx, dy); |
| var localZero = thisGroup.transformCoordToLocal(0, 0); |
| return [localD[0] - localZero[0], localD[1] - localZero[1]]; |
| } |
| |
| function clipByPanel(controller, cover, data) { |
| var panel = getPanelByCover(controller, cover); |
| return panel && panel !== true ? panel.clipPath(data, controller._transform) : zrUtil.clone(data); |
| } |
| |
| 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 resetCursor(controller, e, localCursorPoint) { |
| if ( // Check active |
| !controller._brushType // resetCursor should be always called when mouse is in zr area, |
| // but not called when mouse is out of zr area to avoid bad influence |
| // if `mousemove`, `mouseup` are triggered from `document` event. |
| || isOutsideZrArea(controller, e)) { |
| return; |
| } |
| |
| var zr = controller._zr; |
| var covers = controller._covers; |
| var currPanel = getPanelByPoint(controller, e, localCursorPoint); // Check whether in covers. |
| |
| if (!controller._dragging) { |
| for (var i = 0; i < covers.length; i++) { |
| var brushOption = covers[i].__brushOption; |
| |
| if (currPanel && (currPanel === true || brushOption.panelId === currPanel.panelId) && coverRenderers[brushOption.brushType].contain(covers[i], localCursorPoint[0], localCursorPoint[1])) { |
| // Use cursor style set on cover. |
| return; |
| } |
| } |
| } |
| |
| currPanel && zr.setCursorStyle('crosshair'); |
| } |
| |
| function preventDefault(e) { |
| var rawE = e.event; |
| rawE.preventDefault && rawE.preventDefault(); |
| } |
| |
| function mainShapeContain(cover, x, y) { |
| return cover.childOfName('main').contain(x, y); |
| } |
| |
| function updateCoverByMouse(controller, e, localCursorPoint, isEnd) { |
| var creatingCover = controller._creatingCover; |
| var panel = controller._creatingPanel; |
| var thisBrushOption = controller._brushOption; |
| var eventParams; |
| |
| controller._track.push(localCursorPoint.slice()); |
| |
| if (shouldShowCover(controller) || creatingCover) { |
| if (panel && !creatingCover) { |
| thisBrushOption.brushMode === 'single' && clearCovers(controller); |
| var brushOption = zrUtil.clone(thisBrushOption); |
| brushOption.brushType = determineBrushType(brushOption.brushType, panel); |
| brushOption.panelId = panel === true ? null : panel.panelId; |
| creatingCover = controller._creatingCover = createCover(controller, brushOption); |
| |
| controller._covers.push(creatingCover); |
| } |
| |
| if (creatingCover) { |
| var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)]; |
| var coverBrushOption = creatingCover.__brushOption; |
| coverBrushOption.range = coverRenderer.getCreatingRange(clipByPanel(controller, creatingCover, controller._track)); |
| |
| if (isEnd) { |
| endCreating(controller, creatingCover); |
| coverRenderer.updateCommon(controller, creatingCover); |
| } |
| |
| updateCoverShape(controller, creatingCover); |
| eventParams = { |
| isEnd: isEnd |
| }; |
| } |
| } else if (isEnd && 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). |
| // Only some cover removed, trigger action, but not every click trigger action. |
| if (getPanelByPoint(controller, e, localCursorPoint) && clearCovers(controller)) { |
| eventParams = { |
| isEnd: isEnd, |
| removeOnClick: true |
| }; |
| } |
| } |
| |
| return eventParams; |
| } |
| |
| function determineBrushType(brushType, panel) { |
| if (brushType === 'auto') { |
| return panel.defaultBrushType; |
| } |
| |
| return brushType; |
| } |
| |
| var pointerHandlers = { |
| mousedown: function (e) { |
| if (this._dragging) { |
| // In case some browser do not support globalOut, |
| // and release mose out side the browser. |
| handleDragEnd(this, e); |
| } else if (!e.target || !e.target.draggable) { |
| preventDefault(e); |
| var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY); |
| this._creatingCover = null; |
| var panel = this._creatingPanel = getPanelByPoint(this, e, localCursorPoint); |
| |
| if (panel) { |
| this._dragging = true; |
| this._track = [localCursorPoint.slice()]; |
| } |
| } |
| }, |
| mousemove: function (e) { |
| var x = e.offsetX; |
| var y = e.offsetY; |
| var localCursorPoint = this.group.transformCoordToLocal(x, y); |
| resetCursor(this, e, localCursorPoint); |
| |
| if (this._dragging) { |
| preventDefault(e); |
| var eventParams = updateCoverByMouse(this, e, localCursorPoint, false); |
| eventParams && trigger(this, eventParams); |
| } |
| }, |
| mouseup: function (e) { |
| handleDragEnd(this, e); |
| } |
| }; |
| |
| function handleDragEnd(controller, e) { |
| if (controller._dragging) { |
| preventDefault(e); |
| var x = e.offsetX; |
| var y = e.offsetY; |
| var localCursorPoint = controller.group.transformCoordToLocal(x, y); |
| var eventParams = updateCoverByMouse(controller, e, localCursorPoint, true); |
| controller._dragging = false; |
| controller._track = []; |
| controller._creatingCover = null; // trigger event shoule be at final, after procedure will be nested. |
| |
| eventParams && trigger(controller, eventParams); |
| } |
| } |
| |
| function isOutsideZrArea(controller, x, y) { |
| var zr = controller._zr; |
| return x < 0 || x > zr.getWidth() || y < 0 || y > zr.getHeight(); |
| } |
| /** |
| * key: brushType |
| * @type {Object} |
| */ |
| |
| |
| var coverRenderers = { |
| lineX: getLineRenderer(0), |
| lineY: getLineRenderer(1), |
| rect: { |
| createCover: function (controller, brushOption) { |
| return createBaseRectCover(curry(driftRect, function (range) { |
| return range; |
| }, function (range) { |
| return range; |
| }), controller, brushOption, ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw']); |
| }, |
| getCreatingRange: function (localTrack) { |
| var ends = getTrackEnds(localTrack); |
| return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]); |
| }, |
| updateCoverShape: function (controller, cover, localRange, brushOption) { |
| updateBaseRect(controller, cover, localRange, brushOption); |
| }, |
| updateCommon: updateCommon, |
| contain: mainShapeContain |
| }, |
| polygon: { |
| createCover: function (controller, brushOption) { |
| var cover = new graphic.Group(); // Do not use graphic.Polygon because graphic.Polyline do not close the |
| // border of the shape when drawing, which is a better experience for user. |
| |
| cover.add(new graphic.Polyline({ |
| name: 'main', |
| style: makeStyle(brushOption), |
| silent: true |
| })); |
| return cover; |
| }, |
| getCreatingRange: function (localTrack) { |
| return localTrack; |
| }, |
| endCreating: function (controller, cover) { |
| cover.remove(cover.childAt(0)); // Use graphic.Polygon close the shape. |
| |
| cover.add(new graphic.Polygon({ |
| name: 'main', |
| draggable: true, |
| drift: curry(driftPolygon, controller, cover), |
| ondragend: curry(trigger, controller, { |
| isEnd: true |
| }) |
| })); |
| }, |
| updateCoverShape: function (controller, cover, localRange, brushOption) { |
| cover.childAt(0).setShape({ |
| points: clipByPanel(controller, cover, localRange) |
| }); |
| }, |
| updateCommon: updateCommon, |
| contain: mainShapeContain |
| } |
| }; |
| |
| function getLineRenderer(xyIndex) { |
| return { |
| createCover: function (controller, brushOption) { |
| return createBaseRectCover(curry(driftRect, function (range) { |
| var rectRange = [range, [0, 100]]; |
| xyIndex && rectRange.reverse(); |
| return rectRange; |
| }, function (rectRange) { |
| return rectRange[xyIndex]; |
| }), controller, brushOption, [['w', 'e'], ['n', 's']][xyIndex]); |
| }, |
| getCreatingRange: function (localTrack) { |
| var ends = getTrackEnds(localTrack); |
| 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 otherExtent; // If brushWidth not specified, fit the panel. |
| |
| var panel = getPanelByCover(controller, cover); |
| |
| if (panel !== true && panel.getLinearBrushOtherExtent) { |
| otherExtent = panel.getLinearBrushOtherExtent(xyIndex, controller._transform); |
| } else { |
| var zr = controller._zr; |
| otherExtent = [0, [zr.getWidth(), zr.getHeight()][1 - xyIndex]]; |
| } |
| |
| var rectRange = [localRange, otherExtent]; |
| xyIndex && rectRange.reverse(); |
| updateBaseRect(controller, cover, rectRange, brushOption); |
| }, |
| updateCommon: updateCommon, |
| contain: mainShapeContain |
| }; |
| } |
| |
| export default BrushController; |