BrushController.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867
  1. var _config = require("../../config");
  2. var __DEV__ = _config.__DEV__;
  3. var zrUtil = require("zrender/lib/core/util");
  4. var Eventful = require("zrender/lib/mixin/Eventful");
  5. var graphic = require("../../util/graphic");
  6. var interactionMutex = require("./interactionMutex");
  7. var DataDiffer = require("../../data/DataDiffer");
  8. var curry = zrUtil.curry;
  9. var each = zrUtil.each;
  10. var map = zrUtil.map;
  11. var mathMin = Math.min;
  12. var mathMax = Math.max;
  13. var mathPow = Math.pow;
  14. var COVER_Z = 10000;
  15. var UNSELECT_THRESHOLD = 6;
  16. var MIN_RESIZE_LINE_WIDTH = 6;
  17. var MUTEX_RESOURCE_KEY = 'globalPan';
  18. var DIRECTION_MAP = {
  19. w: [0, 0],
  20. e: [0, 1],
  21. n: [1, 0],
  22. s: [1, 1]
  23. };
  24. var CURSOR_MAP = {
  25. w: 'ew',
  26. e: 'ew',
  27. n: 'ns',
  28. s: 'ns',
  29. ne: 'nesw',
  30. sw: 'nesw',
  31. nw: 'nwse',
  32. se: 'nwse'
  33. };
  34. var DEFAULT_BRUSH_OPT = {
  35. brushStyle: {
  36. lineWidth: 2,
  37. stroke: 'rgba(0,0,0,0.3)',
  38. fill: 'rgba(0,0,0,0.1)'
  39. },
  40. transformable: true,
  41. brushMode: 'single',
  42. removeOnClick: false
  43. };
  44. var baseUID = 0;
  45. /**
  46. * @alias module:echarts/component/helper/BrushController
  47. * @constructor
  48. * @mixin {module:zrender/mixin/Eventful}
  49. * @event module:echarts/component/helper/BrushController#brush
  50. * params:
  51. * areas: Array.<Array>, coord relates to container group,
  52. * If no container specified, to global.
  53. * opt {
  54. * isEnd: boolean,
  55. * removeOnClick: boolean
  56. * }
  57. *
  58. * @param {module:zrender/zrender~ZRender} zr
  59. */
  60. function BrushController(zr) {
  61. Eventful.call(this);
  62. /**
  63. * @type {module:zrender/zrender~ZRender}
  64. * @private
  65. */
  66. this._zr = zr;
  67. /**
  68. * @type {module:zrender/container/Group}
  69. * @readOnly
  70. */
  71. this.group = new graphic.Group();
  72. /**
  73. * Only for drawing (after enabledBrush).
  74. * 'line', 'rect', 'polygon' or false
  75. * If passing false/null/undefined, disable brush.
  76. * If passing 'auto', determined by panel.defaultBrushType
  77. * @private
  78. * @type {string}
  79. */
  80. this._brushType;
  81. /**
  82. * Only for drawing (after enabledBrush).
  83. *
  84. * @private
  85. * @type {Object}
  86. */
  87. this._brushOption;
  88. /**
  89. * @private
  90. * @type {Object}
  91. */
  92. this._panels;
  93. /**
  94. * @private
  95. * @type {Array.<nubmer>}
  96. */
  97. this._track = [];
  98. /**
  99. * @private
  100. * @type {boolean}
  101. */
  102. this._dragging;
  103. /**
  104. * @private
  105. * @type {Array}
  106. */
  107. this._covers = [];
  108. /**
  109. * @private
  110. * @type {moudule:zrender/container/Group}
  111. */
  112. this._creatingCover;
  113. /**
  114. * `true` means global panel
  115. * @private
  116. * @type {module:zrender/container/Group|boolean}
  117. */
  118. this._creatingPanel;
  119. /**
  120. * @private
  121. * @type {boolean}
  122. */
  123. this._enableGlobalPan;
  124. /**
  125. * @private
  126. * @type {boolean}
  127. */
  128. /**
  129. * @private
  130. * @type {string}
  131. */
  132. this._uid = 'brushController_' + baseUID++;
  133. /**
  134. * @private
  135. * @type {Object}
  136. */
  137. this._handlers = {};
  138. each(mouseHandlers, function (handler, eventName) {
  139. this._handlers[eventName] = zrUtil.bind(handler, this);
  140. }, this);
  141. }
  142. BrushController.prototype = {
  143. constructor: BrushController,
  144. /**
  145. * If set to null/undefined/false, select disabled.
  146. * @param {Object} brushOption
  147. * @param {string|boolean} brushOption.brushType 'line', 'rect', 'polygon' or false
  148. * If passing false/null/undefined, disable brush.
  149. * If passing 'auto', determined by panel.defaultBrushType.
  150. * ('auto' can not be used in global panel)
  151. * @param {number} [brushOption.brushMode='single'] 'single' or 'multiple'
  152. * @param {boolean} [brushOption.transformable=true]
  153. * @param {boolean} [brushOption.removeOnClick=false]
  154. * @param {Object} [brushOption.brushStyle]
  155. * @param {number} [brushOption.brushStyle.width]
  156. * @param {number} [brushOption.brushStyle.lineWidth]
  157. * @param {string} [brushOption.brushStyle.stroke]
  158. * @param {string} [brushOption.brushStyle.fill]
  159. * @param {number} [brushOption.z]
  160. */
  161. enableBrush: function (brushOption) {
  162. this._brushType && doDisableBrush(this);
  163. brushOption.brushType && doEnableBrush(this, brushOption);
  164. return this;
  165. },
  166. /**
  167. * @param {Array.<Object>} panelOpts If not pass, it is global brush.
  168. * Each items: {
  169. * panelId, // mandatory.
  170. * clipPath, // mandatory. function.
  171. * isTargetByCursor, // mandatory. function.
  172. * defaultBrushType, // optional, only used when brushType is 'auto'.
  173. * getLinearBrushOtherExtent, // optional. function.
  174. * }
  175. */
  176. setPanels: function (panelOpts) {
  177. if (panelOpts && panelOpts.length) {
  178. var panels = this._panels = {};
  179. zrUtil.each(panelOpts, function (panelOpts) {
  180. panels[panelOpts.panelId] = zrUtil.clone(panelOpts);
  181. });
  182. } else {
  183. this._panels = null;
  184. }
  185. return this;
  186. },
  187. /**
  188. * @param {Object} [opt]
  189. * @return {boolean} [opt.enableGlobalPan=false]
  190. */
  191. mount: function (opt) {
  192. opt = opt || {};
  193. this._enableGlobalPan = opt.enableGlobalPan;
  194. var thisGroup = this.group;
  195. this._zr.add(thisGroup);
  196. thisGroup.attr({
  197. position: opt.position || [0, 0],
  198. rotation: opt.rotation || 0,
  199. scale: opt.scale || [1, 1]
  200. });
  201. this._transform = thisGroup.getLocalTransform();
  202. return this;
  203. },
  204. eachCover: function (cb, context) {
  205. each(this._covers, cb, context);
  206. },
  207. /**
  208. * Update covers.
  209. * @param {Array.<Object>} brushOptionList Like:
  210. * [
  211. * {id: 'xx', brushType: 'line', range: [23, 44], brushStyle, transformable},
  212. * {id: 'yy', brushType: 'rect', range: [[23, 44], [23, 54]]},
  213. * ...
  214. * ]
  215. * `brushType` is required in each cover info. (can not be 'auto')
  216. * `id` is not mandatory.
  217. * `brushStyle`, `transformable` is not mandatory, use DEFAULT_BRUSH_OPT by default.
  218. * If brushOptionList is null/undefined, all covers removed.
  219. */
  220. updateCovers: function (brushOptionList) {
  221. brushOptionList = zrUtil.map(brushOptionList, function (brushOption) {
  222. return zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true);
  223. });
  224. var tmpIdPrefix = '\0-brush-index-';
  225. var oldCovers = this._covers;
  226. var newCovers = this._covers = [];
  227. var controller = this;
  228. var creatingCover = this._creatingCover;
  229. new DataDiffer(oldCovers, brushOptionList, oldGetKey, getKey).add(addOrUpdate).update(addOrUpdate).remove(remove).execute();
  230. return this;
  231. function getKey(brushOption, index) {
  232. return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index) + '-' + brushOption.brushType;
  233. }
  234. function oldGetKey(cover, index) {
  235. return getKey(cover.__brushOption, index);
  236. }
  237. function addOrUpdate(newIndex, oldIndex) {
  238. var newBrushOption = brushOptionList[newIndex]; // Consider setOption in event listener of brushSelect,
  239. // where updating cover when creating should be forbiden.
  240. if (oldIndex != null && oldCovers[oldIndex] === creatingCover) {
  241. newCovers[newIndex] = oldCovers[oldIndex];
  242. } else {
  243. var cover = newCovers[newIndex] = oldIndex != null ? (oldCovers[oldIndex].__brushOption = newBrushOption, oldCovers[oldIndex]) : endCreating(controller, createCover(controller, newBrushOption));
  244. updateCoverAfterCreation(controller, cover);
  245. }
  246. }
  247. function remove(oldIndex) {
  248. if (oldCovers[oldIndex] !== creatingCover) {
  249. controller.group.remove(oldCovers[oldIndex]);
  250. }
  251. }
  252. },
  253. unmount: function () {
  254. this.enableBrush(false); // container may 'removeAll' outside.
  255. clearCovers(this);
  256. this._zr.remove(this.group);
  257. return this;
  258. },
  259. dispose: function () {
  260. this.unmount();
  261. this.off();
  262. }
  263. };
  264. zrUtil.mixin(BrushController, Eventful);
  265. function doEnableBrush(controller, brushOption) {
  266. var zr = controller._zr; // Consider roam, which takes globalPan too.
  267. if (!controller._enableGlobalPan) {
  268. interactionMutex.take(zr, MUTEX_RESOURCE_KEY, controller._uid);
  269. }
  270. each(controller._handlers, function (handler, eventName) {
  271. zr.on(eventName, handler);
  272. });
  273. controller._brushType = brushOption.brushType;
  274. controller._brushOption = zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true);
  275. }
  276. function doDisableBrush(controller) {
  277. var zr = controller._zr;
  278. interactionMutex.release(zr, MUTEX_RESOURCE_KEY, controller._uid);
  279. each(controller._handlers, function (handler, eventName) {
  280. zr.off(eventName, handler);
  281. });
  282. controller._brushType = controller._brushOption = null;
  283. }
  284. function createCover(controller, brushOption) {
  285. var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption);
  286. cover.__brushOption = brushOption;
  287. updateZ(cover, brushOption);
  288. controller.group.add(cover);
  289. return cover;
  290. }
  291. function endCreating(controller, creatingCover) {
  292. var coverRenderer = getCoverRenderer(creatingCover);
  293. if (coverRenderer.endCreating) {
  294. coverRenderer.endCreating(controller, creatingCover);
  295. updateZ(creatingCover, creatingCover.__brushOption);
  296. }
  297. return creatingCover;
  298. }
  299. function updateCoverShape(controller, cover) {
  300. var brushOption = cover.__brushOption;
  301. getCoverRenderer(cover).updateCoverShape(controller, cover, brushOption.range, brushOption);
  302. }
  303. function updateZ(cover, brushOption) {
  304. var z = brushOption.z;
  305. z == null && (z = COVER_Z);
  306. cover.traverse(function (el) {
  307. el.z = z;
  308. el.z2 = z; // Consider in given container.
  309. });
  310. }
  311. function updateCoverAfterCreation(controller, cover) {
  312. getCoverRenderer(cover).updateCommon(controller, cover);
  313. updateCoverShape(controller, cover);
  314. }
  315. function getCoverRenderer(cover) {
  316. return coverRenderers[cover.__brushOption.brushType];
  317. } // return target panel or `true` (means global panel)
  318. function getPanelByPoint(controller, e, localCursorPoint) {
  319. var panels = controller._panels;
  320. if (!panels) {
  321. return true; // Global panel
  322. }
  323. var panel;
  324. var transform = controller._transform;
  325. each(panels, function (pn) {
  326. pn.isTargetByCursor(e, localCursorPoint, transform) && (panel = pn);
  327. });
  328. return panel;
  329. } // Return a panel or true
  330. function getPanelByCover(controller, cover) {
  331. var panels = controller._panels;
  332. if (!panels) {
  333. return true; // Global panel
  334. }
  335. var panelId = cover.__brushOption.panelId; // User may give cover without coord sys info,
  336. // which is then treated as global panel.
  337. return panelId != null ? panels[panelId] : true;
  338. }
  339. function clearCovers(controller) {
  340. var covers = controller._covers;
  341. var originalLength = covers.length;
  342. each(covers, function (cover) {
  343. controller.group.remove(cover);
  344. }, controller);
  345. covers.length = 0;
  346. return !!originalLength;
  347. }
  348. function trigger(controller, opt) {
  349. var areas = map(controller._covers, function (cover) {
  350. var brushOption = cover.__brushOption;
  351. var range = zrUtil.clone(brushOption.range);
  352. return {
  353. brushType: brushOption.brushType,
  354. panelId: brushOption.panelId,
  355. range: range
  356. };
  357. });
  358. controller.trigger('brush', areas, {
  359. isEnd: !!opt.isEnd,
  360. removeOnClick: !!opt.removeOnClick
  361. });
  362. }
  363. function shouldShowCover(controller) {
  364. var track = controller._track;
  365. if (!track.length) {
  366. return false;
  367. }
  368. var p2 = track[track.length - 1];
  369. var p1 = track[0];
  370. var dx = p2[0] - p1[0];
  371. var dy = p2[1] - p1[1];
  372. var dist = mathPow(dx * dx + dy * dy, 0.5);
  373. return dist > UNSELECT_THRESHOLD;
  374. }
  375. function getTrackEnds(track) {
  376. var tail = track.length - 1;
  377. tail < 0 && (tail = 0);
  378. return [track[0], track[tail]];
  379. }
  380. function createBaseRectCover(doDrift, controller, brushOption, edgeNames) {
  381. var cover = new graphic.Group();
  382. cover.add(new graphic.Rect({
  383. name: 'main',
  384. style: makeStyle(brushOption),
  385. silent: true,
  386. draggable: true,
  387. cursor: 'move',
  388. drift: curry(doDrift, controller, cover, 'nswe'),
  389. ondragend: curry(trigger, controller, {
  390. isEnd: true
  391. })
  392. }));
  393. each(edgeNames, function (name) {
  394. cover.add(new graphic.Rect({
  395. name: name,
  396. style: {
  397. opacity: 0
  398. },
  399. draggable: true,
  400. silent: true,
  401. invisible: true,
  402. drift: curry(doDrift, controller, cover, name),
  403. ondragend: curry(trigger, controller, {
  404. isEnd: true
  405. })
  406. }));
  407. });
  408. return cover;
  409. }
  410. function updateBaseRect(controller, cover, localRange, brushOption) {
  411. var lineWidth = brushOption.brushStyle.lineWidth || 0;
  412. var handleSize = mathMax(lineWidth, MIN_RESIZE_LINE_WIDTH);
  413. var x = localRange[0][0];
  414. var y = localRange[1][0];
  415. var xa = x - lineWidth / 2;
  416. var ya = y - lineWidth / 2;
  417. var x2 = localRange[0][1];
  418. var y2 = localRange[1][1];
  419. var x2a = x2 - handleSize + lineWidth / 2;
  420. var y2a = y2 - handleSize + lineWidth / 2;
  421. var width = x2 - x;
  422. var height = y2 - y;
  423. var widtha = width + lineWidth;
  424. var heighta = height + lineWidth;
  425. updateRectShape(controller, cover, 'main', x, y, width, height);
  426. if (brushOption.transformable) {
  427. updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta);
  428. updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta);
  429. updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize);
  430. updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize);
  431. updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize);
  432. updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize);
  433. updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize);
  434. updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize);
  435. }
  436. }
  437. function updateCommon(controller, cover) {
  438. var brushOption = cover.__brushOption;
  439. var transformable = brushOption.transformable;
  440. var mainEl = cover.childAt(0);
  441. mainEl.useStyle(makeStyle(brushOption));
  442. mainEl.attr({
  443. silent: !transformable,
  444. cursor: transformable ? 'move' : 'default'
  445. });
  446. each(['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'], function (name) {
  447. var el = cover.childOfName(name);
  448. var globalDir = getGlobalDirection(controller, name);
  449. el && el.attr({
  450. silent: !transformable,
  451. invisible: !transformable,
  452. cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null
  453. });
  454. });
  455. }
  456. function updateRectShape(controller, cover, name, x, y, w, h) {
  457. var el = cover.childOfName(name);
  458. el && el.setShape(pointsToRect(clipByPanel(controller, cover, [[x, y], [x + w, y + h]])));
  459. }
  460. function makeStyle(brushOption) {
  461. return zrUtil.defaults({
  462. strokeNoScale: true
  463. }, brushOption.brushStyle);
  464. }
  465. function formatRectRange(x, y, x2, y2) {
  466. var min = [mathMin(x, x2), mathMin(y, y2)];
  467. var max = [mathMax(x, x2), mathMax(y, y2)];
  468. return [[min[0], max[0]], // x range
  469. [min[1], max[1]] // y range
  470. ];
  471. }
  472. function getTransform(controller) {
  473. return graphic.getTransform(controller.group);
  474. }
  475. function getGlobalDirection(controller, localDirection) {
  476. if (localDirection.length > 1) {
  477. localDirection = localDirection.split('');
  478. var globalDir = [getGlobalDirection(controller, localDirection[0]), getGlobalDirection(controller, localDirection[1])];
  479. (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse();
  480. return globalDir.join('');
  481. } else {
  482. var map = {
  483. w: 'left',
  484. e: 'right',
  485. n: 'top',
  486. s: 'bottom'
  487. };
  488. var inverseMap = {
  489. left: 'w',
  490. right: 'e',
  491. top: 'n',
  492. bottom: 's'
  493. };
  494. var globalDir = graphic.transformDirection(map[localDirection], getTransform(controller));
  495. return inverseMap[globalDir];
  496. }
  497. }
  498. function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy, e) {
  499. var brushOption = cover.__brushOption;
  500. var rectRange = toRectRange(brushOption.range);
  501. var localDelta = toLocalDelta(controller, dx, dy);
  502. each(name.split(''), function (namePart) {
  503. var ind = DIRECTION_MAP[namePart];
  504. rectRange[ind[0]][ind[1]] += localDelta[ind[0]];
  505. });
  506. brushOption.range = fromRectRange(formatRectRange(rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1]));
  507. updateCoverAfterCreation(controller, cover);
  508. trigger(controller, {
  509. isEnd: false
  510. });
  511. }
  512. function driftPolygon(controller, cover, dx, dy, e) {
  513. var range = cover.__brushOption.range;
  514. var localDelta = toLocalDelta(controller, dx, dy);
  515. each(range, function (point) {
  516. point[0] += localDelta[0];
  517. point[1] += localDelta[1];
  518. });
  519. updateCoverAfterCreation(controller, cover);
  520. trigger(controller, {
  521. isEnd: false
  522. });
  523. }
  524. function toLocalDelta(controller, dx, dy) {
  525. var thisGroup = controller.group;
  526. var localD = thisGroup.transformCoordToLocal(dx, dy);
  527. var localZero = thisGroup.transformCoordToLocal(0, 0);
  528. return [localD[0] - localZero[0], localD[1] - localZero[1]];
  529. }
  530. function clipByPanel(controller, cover, data) {
  531. var panel = getPanelByCover(controller, cover);
  532. return panel && panel !== true ? panel.clipPath(data, controller._transform) : zrUtil.clone(data);
  533. }
  534. function pointsToRect(points) {
  535. var xmin = mathMin(points[0][0], points[1][0]);
  536. var ymin = mathMin(points[0][1], points[1][1]);
  537. var xmax = mathMax(points[0][0], points[1][0]);
  538. var ymax = mathMax(points[0][1], points[1][1]);
  539. return {
  540. x: xmin,
  541. y: ymin,
  542. width: xmax - xmin,
  543. height: ymax - ymin
  544. };
  545. }
  546. function resetCursor(controller, e, localCursorPoint) {
  547. // Check active
  548. if (!controller._brushType) {
  549. return;
  550. }
  551. var zr = controller._zr;
  552. var covers = controller._covers;
  553. var currPanel = getPanelByPoint(controller, e, localCursorPoint); // Check whether in covers.
  554. if (!controller._dragging) {
  555. for (var i = 0; i < covers.length; i++) {
  556. var brushOption = covers[i].__brushOption;
  557. if (currPanel && (currPanel === true || brushOption.panelId === currPanel.panelId) && coverRenderers[brushOption.brushType].contain(covers[i], localCursorPoint[0], localCursorPoint[1])) {
  558. // Use cursor style set on cover.
  559. return;
  560. }
  561. }
  562. }
  563. currPanel && zr.setCursorStyle('crosshair');
  564. }
  565. function preventDefault(e) {
  566. var rawE = e.event;
  567. rawE.preventDefault && rawE.preventDefault();
  568. }
  569. function mainShapeContain(cover, x, y) {
  570. return cover.childOfName('main').contain(x, y);
  571. }
  572. function updateCoverByMouse(controller, e, localCursorPoint, isEnd) {
  573. var creatingCover = controller._creatingCover;
  574. var panel = controller._creatingPanel;
  575. var thisBrushOption = controller._brushOption;
  576. var eventParams;
  577. controller._track.push(localCursorPoint.slice());
  578. if (shouldShowCover(controller) || creatingCover) {
  579. if (panel && !creatingCover) {
  580. thisBrushOption.brushMode === 'single' && clearCovers(controller);
  581. var brushOption = zrUtil.clone(thisBrushOption);
  582. brushOption.brushType = determineBrushType(brushOption.brushType, panel);
  583. brushOption.panelId = panel === true ? null : panel.panelId;
  584. creatingCover = controller._creatingCover = createCover(controller, brushOption);
  585. controller._covers.push(creatingCover);
  586. }
  587. if (creatingCover) {
  588. var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)];
  589. var coverBrushOption = creatingCover.__brushOption;
  590. coverBrushOption.range = coverRenderer.getCreatingRange(clipByPanel(controller, creatingCover, controller._track));
  591. if (isEnd) {
  592. endCreating(controller, creatingCover);
  593. coverRenderer.updateCommon(controller, creatingCover);
  594. }
  595. updateCoverShape(controller, creatingCover);
  596. eventParams = {
  597. isEnd: isEnd
  598. };
  599. }
  600. } else if (isEnd && thisBrushOption.brushMode === 'single' && thisBrushOption.removeOnClick) {
  601. // Help user to remove covers easily, only by a tiny drag, in 'single' mode.
  602. // But a single click do not clear covers, because user may have casual
  603. // clicks (for example, click on other component and do not expect covers
  604. // disappear).
  605. // Only some cover removed, trigger action, but not every click trigger action.
  606. if (getPanelByPoint(controller, e, localCursorPoint) && clearCovers(controller)) {
  607. eventParams = {
  608. isEnd: isEnd,
  609. removeOnClick: true
  610. };
  611. }
  612. }
  613. return eventParams;
  614. }
  615. function determineBrushType(brushType, panel) {
  616. if (brushType === 'auto') {
  617. return panel.defaultBrushType;
  618. }
  619. return brushType;
  620. }
  621. var mouseHandlers = {
  622. mousedown: function (e) {
  623. if (this._dragging) {
  624. // In case some browser do not support globalOut,
  625. // and release mose out side the browser.
  626. handleDragEnd.call(this, e);
  627. } else if (!e.target || !e.target.draggable) {
  628. preventDefault(e);
  629. var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
  630. this._creatingCover = null;
  631. var panel = this._creatingPanel = getPanelByPoint(this, e, localCursorPoint);
  632. if (panel) {
  633. this._dragging = true;
  634. this._track = [localCursorPoint.slice()];
  635. }
  636. }
  637. },
  638. mousemove: function (e) {
  639. var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
  640. resetCursor(this, e, localCursorPoint);
  641. if (this._dragging) {
  642. preventDefault(e);
  643. var eventParams = updateCoverByMouse(this, e, localCursorPoint, false);
  644. eventParams && trigger(this, eventParams);
  645. }
  646. },
  647. mouseup: handleDragEnd //,
  648. // FIXME
  649. // in tooltip, globalout should not be triggered.
  650. // globalout: handleDragEnd
  651. };
  652. function handleDragEnd(e) {
  653. if (this._dragging) {
  654. preventDefault(e);
  655. var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
  656. var eventParams = updateCoverByMouse(this, e, localCursorPoint, true);
  657. this._dragging = false;
  658. this._track = [];
  659. this._creatingCover = null; // trigger event shoule be at final, after procedure will be nested.
  660. eventParams && trigger(this, eventParams);
  661. }
  662. }
  663. /**
  664. * key: brushType
  665. * @type {Object}
  666. */
  667. var coverRenderers = {
  668. lineX: getLineRenderer(0),
  669. lineY: getLineRenderer(1),
  670. rect: {
  671. createCover: function (controller, brushOption) {
  672. return createBaseRectCover(curry(driftRect, function (range) {
  673. return range;
  674. }, function (range) {
  675. return range;
  676. }), controller, brushOption, ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw']);
  677. },
  678. getCreatingRange: function (localTrack) {
  679. var ends = getTrackEnds(localTrack);
  680. return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]);
  681. },
  682. updateCoverShape: function (controller, cover, localRange, brushOption) {
  683. updateBaseRect(controller, cover, localRange, brushOption);
  684. },
  685. updateCommon: updateCommon,
  686. contain: mainShapeContain
  687. },
  688. polygon: {
  689. createCover: function (controller, brushOption) {
  690. var cover = new graphic.Group(); // Do not use graphic.Polygon because graphic.Polyline do not close the
  691. // border of the shape when drawing, which is a better experience for user.
  692. cover.add(new graphic.Polyline({
  693. name: 'main',
  694. style: makeStyle(brushOption),
  695. silent: true
  696. }));
  697. return cover;
  698. },
  699. getCreatingRange: function (localTrack) {
  700. return localTrack;
  701. },
  702. endCreating: function (controller, cover) {
  703. cover.remove(cover.childAt(0)); // Use graphic.Polygon close the shape.
  704. cover.add(new graphic.Polygon({
  705. name: 'main',
  706. draggable: true,
  707. drift: curry(driftPolygon, controller, cover),
  708. ondragend: curry(trigger, controller, {
  709. isEnd: true
  710. })
  711. }));
  712. },
  713. updateCoverShape: function (controller, cover, localRange, brushOption) {
  714. cover.childAt(0).setShape({
  715. points: clipByPanel(controller, cover, localRange)
  716. });
  717. },
  718. updateCommon: updateCommon,
  719. contain: mainShapeContain
  720. }
  721. };
  722. function getLineRenderer(xyIndex) {
  723. return {
  724. createCover: function (controller, brushOption) {
  725. return createBaseRectCover(curry(driftRect, function (range) {
  726. var rectRange = [range, [0, 100]];
  727. xyIndex && rectRange.reverse();
  728. return rectRange;
  729. }, function (rectRange) {
  730. return rectRange[xyIndex];
  731. }), controller, brushOption, [['w', 'e'], ['n', 's']][xyIndex]);
  732. },
  733. getCreatingRange: function (localTrack) {
  734. var ends = getTrackEnds(localTrack);
  735. var min = mathMin(ends[0][xyIndex], ends[1][xyIndex]);
  736. var max = mathMax(ends[0][xyIndex], ends[1][xyIndex]);
  737. return [min, max];
  738. },
  739. updateCoverShape: function (controller, cover, localRange, brushOption) {
  740. var otherExtent; // If brushWidth not specified, fit the panel.
  741. var panel = getPanelByCover(controller, cover);
  742. if (panel !== true && panel.getLinearBrushOtherExtent) {
  743. otherExtent = panel.getLinearBrushOtherExtent(xyIndex, controller._transform);
  744. } else {
  745. var zr = controller._zr;
  746. otherExtent = [0, [zr.getWidth(), zr.getHeight()][1 - xyIndex]];
  747. }
  748. var rectRange = [localRange, otherExtent];
  749. xyIndex && rectRange.reverse();
  750. updateBaseRect(controller, cover, rectRange, brushOption);
  751. },
  752. updateCommon: updateCommon,
  753. contain: mainShapeContain
  754. };
  755. }
  756. var _default = BrushController;
  757. module.exports = _default;