BrushController.js 27 KB

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