SliderTimelineView.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. var zrUtil = require("zrender/lib/core/util");
  2. var BoundingRect = require("zrender/lib/core/BoundingRect");
  3. var matrix = require("zrender/lib/core/matrix");
  4. var graphic = require("../../util/graphic");
  5. var layout = require("../../util/layout");
  6. var TimelineView = require("./TimelineView");
  7. var TimelineAxis = require("./TimelineAxis");
  8. var _symbol = require("../../util/symbol");
  9. var createSymbol = _symbol.createSymbol;
  10. var axisHelper = require("../../coord/axisHelper");
  11. var numberUtil = require("../../util/number");
  12. var _format = require("../../util/format");
  13. var encodeHTML = _format.encodeHTML;
  14. var bind = zrUtil.bind;
  15. var each = zrUtil.each;
  16. var PI = Math.PI;
  17. var _default = TimelineView.extend({
  18. type: 'timeline.slider',
  19. init: function (ecModel, api) {
  20. this.api = api;
  21. /**
  22. * @private
  23. * @type {module:echarts/component/timeline/TimelineAxis}
  24. */
  25. this._axis;
  26. /**
  27. * @private
  28. * @type {module:zrender/core/BoundingRect}
  29. */
  30. this._viewRect;
  31. /**
  32. * @type {number}
  33. */
  34. this._timer;
  35. /**
  36. * @type {module:zrender/Element}
  37. */
  38. this._currentPointer;
  39. /**
  40. * @type {module:zrender/container/Group}
  41. */
  42. this._mainGroup;
  43. /**
  44. * @type {module:zrender/container/Group}
  45. */
  46. this._labelGroup;
  47. },
  48. /**
  49. * @override
  50. */
  51. render: function (timelineModel, ecModel, api, payload) {
  52. this.model = timelineModel;
  53. this.api = api;
  54. this.ecModel = ecModel;
  55. this.group.removeAll();
  56. if (timelineModel.get('show', true)) {
  57. var layoutInfo = this._layout(timelineModel, api);
  58. var mainGroup = this._createGroup('mainGroup');
  59. var labelGroup = this._createGroup('labelGroup');
  60. /**
  61. * @private
  62. * @type {module:echarts/component/timeline/TimelineAxis}
  63. */
  64. var axis = this._axis = this._createAxis(layoutInfo, timelineModel);
  65. timelineModel.formatTooltip = function (dataIndex) {
  66. return encodeHTML(axis.scale.getLabel(dataIndex));
  67. };
  68. each(['AxisLine', 'AxisTick', 'Control', 'CurrentPointer'], function (name) {
  69. this['_render' + name](layoutInfo, mainGroup, axis, timelineModel);
  70. }, this);
  71. this._renderAxisLabel(layoutInfo, labelGroup, axis, timelineModel);
  72. this._position(layoutInfo, timelineModel);
  73. }
  74. this._doPlayStop();
  75. },
  76. /**
  77. * @override
  78. */
  79. remove: function () {
  80. this._clearTimer();
  81. this.group.removeAll();
  82. },
  83. /**
  84. * @override
  85. */
  86. dispose: function () {
  87. this._clearTimer();
  88. },
  89. _layout: function (timelineModel, api) {
  90. var labelPosOpt = timelineModel.get('label.normal.position');
  91. var orient = timelineModel.get('orient');
  92. var viewRect = getViewRect(timelineModel, api); // Auto label offset.
  93. if (labelPosOpt == null || labelPosOpt === 'auto') {
  94. labelPosOpt = orient === 'horizontal' ? viewRect.y + viewRect.height / 2 < api.getHeight() / 2 ? '-' : '+' : viewRect.x + viewRect.width / 2 < api.getWidth() / 2 ? '+' : '-';
  95. } else if (isNaN(labelPosOpt)) {
  96. labelPosOpt = {
  97. horizontal: {
  98. top: '-',
  99. bottom: '+'
  100. },
  101. vertical: {
  102. left: '-',
  103. right: '+'
  104. }
  105. }[orient][labelPosOpt];
  106. }
  107. var labelAlignMap = {
  108. horizontal: 'center',
  109. vertical: labelPosOpt >= 0 || labelPosOpt === '+' ? 'left' : 'right'
  110. };
  111. var labelBaselineMap = {
  112. horizontal: labelPosOpt >= 0 || labelPosOpt === '+' ? 'top' : 'bottom',
  113. vertical: 'middle'
  114. };
  115. var rotationMap = {
  116. horizontal: 0,
  117. vertical: PI / 2
  118. }; // Position
  119. var mainLength = orient === 'vertical' ? viewRect.height : viewRect.width;
  120. var controlModel = timelineModel.getModel('controlStyle');
  121. var showControl = controlModel.get('show');
  122. var controlSize = showControl ? controlModel.get('itemSize') : 0;
  123. var controlGap = showControl ? controlModel.get('itemGap') : 0;
  124. var sizePlusGap = controlSize + controlGap; // Special label rotate.
  125. var labelRotation = timelineModel.get('label.normal.rotate') || 0;
  126. labelRotation = labelRotation * PI / 180; // To radian.
  127. var playPosition;
  128. var prevBtnPosition;
  129. var nextBtnPosition;
  130. var axisExtent;
  131. var controlPosition = controlModel.get('position', true);
  132. var showControl = controlModel.get('show', true);
  133. var showPlayBtn = showControl && controlModel.get('showPlayBtn', true);
  134. var showPrevBtn = showControl && controlModel.get('showPrevBtn', true);
  135. var showNextBtn = showControl && controlModel.get('showNextBtn', true);
  136. var xLeft = 0;
  137. var xRight = mainLength; // position[0] means left, position[1] means middle.
  138. if (controlPosition === 'left' || controlPosition === 'bottom') {
  139. showPlayBtn && (playPosition = [0, 0], xLeft += sizePlusGap);
  140. showPrevBtn && (prevBtnPosition = [xLeft, 0], xLeft += sizePlusGap);
  141. showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);
  142. } else {
  143. // 'top' 'right'
  144. showPlayBtn && (playPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);
  145. showPrevBtn && (prevBtnPosition = [0, 0], xLeft += sizePlusGap);
  146. showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);
  147. }
  148. axisExtent = [xLeft, xRight];
  149. if (timelineModel.get('inverse')) {
  150. axisExtent.reverse();
  151. }
  152. return {
  153. viewRect: viewRect,
  154. mainLength: mainLength,
  155. orient: orient,
  156. rotation: rotationMap[orient],
  157. labelRotation: labelRotation,
  158. labelPosOpt: labelPosOpt,
  159. labelAlign: timelineModel.get('label.normal.align') || labelAlignMap[orient],
  160. labelBaseline: timelineModel.get('label.normal.verticalAlign') || timelineModel.get('label.normal.baseline') || labelBaselineMap[orient],
  161. // Based on mainGroup.
  162. playPosition: playPosition,
  163. prevBtnPosition: prevBtnPosition,
  164. nextBtnPosition: nextBtnPosition,
  165. axisExtent: axisExtent,
  166. controlSize: controlSize,
  167. controlGap: controlGap
  168. };
  169. },
  170. _position: function (layoutInfo, timelineModel) {
  171. // Position is be called finally, because bounding rect is needed for
  172. // adapt content to fill viewRect (auto adapt offset).
  173. // Timeline may be not all in the viewRect when 'offset' is specified
  174. // as a number, because it is more appropriate that label aligns at
  175. // 'offset' but not the other edge defined by viewRect.
  176. var mainGroup = this._mainGroup;
  177. var labelGroup = this._labelGroup;
  178. var viewRect = layoutInfo.viewRect;
  179. if (layoutInfo.orient === 'vertical') {
  180. // transfrom to horizontal, inverse rotate by left-top point.
  181. var m = matrix.create();
  182. var rotateOriginX = viewRect.x;
  183. var rotateOriginY = viewRect.y + viewRect.height;
  184. matrix.translate(m, m, [-rotateOriginX, -rotateOriginY]);
  185. matrix.rotate(m, m, -PI / 2);
  186. matrix.translate(m, m, [rotateOriginX, rotateOriginY]);
  187. viewRect = viewRect.clone();
  188. viewRect.applyTransform(m);
  189. }
  190. var viewBound = getBound(viewRect);
  191. var mainBound = getBound(mainGroup.getBoundingRect());
  192. var labelBound = getBound(labelGroup.getBoundingRect());
  193. var mainPosition = mainGroup.position;
  194. var labelsPosition = labelGroup.position;
  195. labelsPosition[0] = mainPosition[0] = viewBound[0][0];
  196. var labelPosOpt = layoutInfo.labelPosOpt;
  197. if (isNaN(labelPosOpt)) {
  198. // '+' or '-'
  199. var mainBoundIdx = labelPosOpt === '+' ? 0 : 1;
  200. toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx);
  201. toBound(labelsPosition, labelBound, viewBound, 1, 1 - mainBoundIdx);
  202. } else {
  203. var mainBoundIdx = labelPosOpt >= 0 ? 0 : 1;
  204. toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx);
  205. labelsPosition[1] = mainPosition[1] + labelPosOpt;
  206. }
  207. mainGroup.attr('position', mainPosition);
  208. labelGroup.attr('position', labelsPosition);
  209. mainGroup.rotation = labelGroup.rotation = layoutInfo.rotation;
  210. setOrigin(mainGroup);
  211. setOrigin(labelGroup);
  212. function setOrigin(targetGroup) {
  213. var pos = targetGroup.position;
  214. targetGroup.origin = [viewBound[0][0] - pos[0], viewBound[1][0] - pos[1]];
  215. }
  216. function getBound(rect) {
  217. // [[xmin, xmax], [ymin, ymax]]
  218. return [[rect.x, rect.x + rect.width], [rect.y, rect.y + rect.height]];
  219. }
  220. function toBound(fromPos, from, to, dimIdx, boundIdx) {
  221. fromPos[dimIdx] += to[dimIdx][boundIdx] - from[dimIdx][boundIdx];
  222. }
  223. },
  224. _createAxis: function (layoutInfo, timelineModel) {
  225. var data = timelineModel.getData();
  226. var axisType = timelineModel.get('axisType');
  227. var scale = axisHelper.createScaleByModel(timelineModel, axisType);
  228. var dataExtent = data.getDataExtent('value');
  229. scale.setExtent(dataExtent[0], dataExtent[1]);
  230. this._customizeScale(scale, data);
  231. scale.niceTicks();
  232. var axis = new TimelineAxis('value', scale, layoutInfo.axisExtent, axisType);
  233. axis.model = timelineModel;
  234. return axis;
  235. },
  236. _customizeScale: function (scale, data) {
  237. scale.getTicks = function () {
  238. return data.mapArray(['value'], function (value) {
  239. return value;
  240. });
  241. };
  242. scale.getTicksLabels = function () {
  243. return zrUtil.map(this.getTicks(), scale.getLabel, scale);
  244. };
  245. },
  246. _createGroup: function (name) {
  247. var newGroup = this['_' + name] = new graphic.Group();
  248. this.group.add(newGroup);
  249. return newGroup;
  250. },
  251. _renderAxisLine: function (layoutInfo, group, axis, timelineModel) {
  252. var axisExtent = axis.getExtent();
  253. if (!timelineModel.get('lineStyle.show')) {
  254. return;
  255. }
  256. group.add(new graphic.Line({
  257. shape: {
  258. x1: axisExtent[0],
  259. y1: 0,
  260. x2: axisExtent[1],
  261. y2: 0
  262. },
  263. style: zrUtil.extend({
  264. lineCap: 'round'
  265. }, timelineModel.getModel('lineStyle').getLineStyle()),
  266. silent: true,
  267. z2: 1
  268. }));
  269. },
  270. /**
  271. * @private
  272. */
  273. _renderAxisTick: function (layoutInfo, group, axis, timelineModel) {
  274. var data = timelineModel.getData();
  275. var ticks = axis.scale.getTicks();
  276. each(ticks, function (value, dataIndex) {
  277. var tickCoord = axis.dataToCoord(value);
  278. var itemModel = data.getItemModel(dataIndex);
  279. var itemStyleModel = itemModel.getModel('itemStyle.normal');
  280. var hoverStyleModel = itemModel.getModel('itemStyle.emphasis');
  281. var symbolOpt = {
  282. position: [tickCoord, 0],
  283. onclick: bind(this._changeTimeline, this, dataIndex)
  284. };
  285. var el = giveSymbol(itemModel, itemStyleModel, group, symbolOpt);
  286. graphic.setHoverStyle(el, hoverStyleModel.getItemStyle());
  287. if (itemModel.get('tooltip')) {
  288. el.dataIndex = dataIndex;
  289. el.dataModel = timelineModel;
  290. } else {
  291. el.dataIndex = el.dataModel = null;
  292. }
  293. }, this);
  294. },
  295. /**
  296. * @private
  297. */
  298. _renderAxisLabel: function (layoutInfo, group, axis, timelineModel) {
  299. var labelModel = timelineModel.getModel('label.normal');
  300. if (!labelModel.get('show')) {
  301. return;
  302. }
  303. var data = timelineModel.getData();
  304. var ticks = axis.scale.getTicks();
  305. var labels = axisHelper.getFormattedLabels(axis, labelModel.get('formatter'));
  306. var labelInterval = axis.getLabelInterval();
  307. each(ticks, function (tick, dataIndex) {
  308. if (axis.isLabelIgnored(dataIndex, labelInterval)) {
  309. return;
  310. }
  311. var itemModel = data.getItemModel(dataIndex);
  312. var normalLabelModel = itemModel.getModel('label.normal');
  313. var hoverLabelModel = itemModel.getModel('label.emphasis');
  314. var tickCoord = axis.dataToCoord(tick);
  315. var textEl = new graphic.Text({
  316. position: [tickCoord, 0],
  317. rotation: layoutInfo.labelRotation - layoutInfo.rotation,
  318. onclick: bind(this._changeTimeline, this, dataIndex),
  319. silent: false
  320. });
  321. graphic.setTextStyle(textEl.style, normalLabelModel, {
  322. text: labels[dataIndex],
  323. textAlign: layoutInfo.labelAlign,
  324. textVerticalAlign: layoutInfo.labelBaseline
  325. });
  326. group.add(textEl);
  327. graphic.setHoverStyle(textEl, graphic.setTextStyle({}, hoverLabelModel));
  328. }, this);
  329. },
  330. /**
  331. * @private
  332. */
  333. _renderControl: function (layoutInfo, group, axis, timelineModel) {
  334. var controlSize = layoutInfo.controlSize;
  335. var rotation = layoutInfo.rotation;
  336. var itemStyle = timelineModel.getModel('controlStyle.normal').getItemStyle();
  337. var hoverStyle = timelineModel.getModel('controlStyle.emphasis').getItemStyle();
  338. var rect = [0, -controlSize / 2, controlSize, controlSize];
  339. var playState = timelineModel.getPlayState();
  340. var inverse = timelineModel.get('inverse', true);
  341. makeBtn(layoutInfo.nextBtnPosition, 'controlStyle.nextIcon', bind(this._changeTimeline, this, inverse ? '-' : '+'));
  342. makeBtn(layoutInfo.prevBtnPosition, 'controlStyle.prevIcon', bind(this._changeTimeline, this, inverse ? '+' : '-'));
  343. makeBtn(layoutInfo.playPosition, 'controlStyle.' + (playState ? 'stopIcon' : 'playIcon'), bind(this._handlePlayClick, this, !playState), true);
  344. function makeBtn(position, iconPath, onclick, willRotate) {
  345. if (!position) {
  346. return;
  347. }
  348. var opt = {
  349. position: position,
  350. origin: [controlSize / 2, 0],
  351. rotation: willRotate ? -rotation : 0,
  352. rectHover: true,
  353. style: itemStyle,
  354. onclick: onclick
  355. };
  356. var btn = makeIcon(timelineModel, iconPath, rect, opt);
  357. group.add(btn);
  358. graphic.setHoverStyle(btn, hoverStyle);
  359. }
  360. },
  361. _renderCurrentPointer: function (layoutInfo, group, axis, timelineModel) {
  362. var data = timelineModel.getData();
  363. var currentIndex = timelineModel.getCurrentIndex();
  364. var pointerModel = data.getItemModel(currentIndex).getModel('checkpointStyle');
  365. var me = this;
  366. var callback = {
  367. onCreate: function (pointer) {
  368. pointer.draggable = true;
  369. pointer.drift = bind(me._handlePointerDrag, me);
  370. pointer.ondragend = bind(me._handlePointerDragend, me);
  371. pointerMoveTo(pointer, currentIndex, axis, timelineModel, true);
  372. },
  373. onUpdate: function (pointer) {
  374. pointerMoveTo(pointer, currentIndex, axis, timelineModel);
  375. }
  376. }; // Reuse when exists, for animation and drag.
  377. this._currentPointer = giveSymbol(pointerModel, pointerModel, this._mainGroup, {}, this._currentPointer, callback);
  378. },
  379. _handlePlayClick: function (nextState) {
  380. this._clearTimer();
  381. this.api.dispatchAction({
  382. type: 'timelinePlayChange',
  383. playState: nextState,
  384. from: this.uid
  385. });
  386. },
  387. _handlePointerDrag: function (dx, dy, e) {
  388. this._clearTimer();
  389. this._pointerChangeTimeline([e.offsetX, e.offsetY]);
  390. },
  391. _handlePointerDragend: function (e) {
  392. this._pointerChangeTimeline([e.offsetX, e.offsetY], true);
  393. },
  394. _pointerChangeTimeline: function (mousePos, trigger) {
  395. var toCoord = this._toAxisCoord(mousePos)[0];
  396. var axis = this._axis;
  397. var axisExtent = numberUtil.asc(axis.getExtent().slice());
  398. toCoord > axisExtent[1] && (toCoord = axisExtent[1]);
  399. toCoord < axisExtent[0] && (toCoord = axisExtent[0]);
  400. this._currentPointer.position[0] = toCoord;
  401. this._currentPointer.dirty();
  402. var targetDataIndex = this._findNearestTick(toCoord);
  403. var timelineModel = this.model;
  404. if (trigger || targetDataIndex !== timelineModel.getCurrentIndex() && timelineModel.get('realtime')) {
  405. this._changeTimeline(targetDataIndex);
  406. }
  407. },
  408. _doPlayStop: function () {
  409. this._clearTimer();
  410. if (this.model.getPlayState()) {
  411. this._timer = setTimeout(bind(handleFrame, this), this.model.get('playInterval'));
  412. }
  413. function handleFrame() {
  414. // Do not cache
  415. var timelineModel = this.model;
  416. this._changeTimeline(timelineModel.getCurrentIndex() + (timelineModel.get('rewind', true) ? -1 : 1));
  417. }
  418. },
  419. _toAxisCoord: function (vertex) {
  420. var trans = this._mainGroup.getLocalTransform();
  421. return graphic.applyTransform(vertex, trans, true);
  422. },
  423. _findNearestTick: function (axisCoord) {
  424. var data = this.model.getData();
  425. var dist = Infinity;
  426. var targetDataIndex;
  427. var axis = this._axis;
  428. data.each(['value'], function (value, dataIndex) {
  429. var coord = axis.dataToCoord(value);
  430. var d = Math.abs(coord - axisCoord);
  431. if (d < dist) {
  432. dist = d;
  433. targetDataIndex = dataIndex;
  434. }
  435. });
  436. return targetDataIndex;
  437. },
  438. _clearTimer: function () {
  439. if (this._timer) {
  440. clearTimeout(this._timer);
  441. this._timer = null;
  442. }
  443. },
  444. _changeTimeline: function (nextIndex) {
  445. var currentIndex = this.model.getCurrentIndex();
  446. if (nextIndex === '+') {
  447. nextIndex = currentIndex + 1;
  448. } else if (nextIndex === '-') {
  449. nextIndex = currentIndex - 1;
  450. }
  451. this.api.dispatchAction({
  452. type: 'timelineChange',
  453. currentIndex: nextIndex,
  454. from: this.uid
  455. });
  456. }
  457. });
  458. function getViewRect(model, api) {
  459. return layout.getLayoutRect(model.getBoxLayoutParams(), {
  460. width: api.getWidth(),
  461. height: api.getHeight()
  462. }, model.get('padding'));
  463. }
  464. function makeIcon(timelineModel, objPath, rect, opts) {
  465. var icon = graphic.makePath(timelineModel.get(objPath).replace(/^path:\/\//, ''), zrUtil.clone(opts || {}), new BoundingRect(rect[0], rect[1], rect[2], rect[3]), 'center');
  466. return icon;
  467. }
  468. /**
  469. * Create symbol or update symbol
  470. * opt: basic position and event handlers
  471. */
  472. function giveSymbol(hostModel, itemStyleModel, group, opt, symbol, callback) {
  473. var color = itemStyleModel.get('color');
  474. if (!symbol) {
  475. var symbolType = hostModel.get('symbol');
  476. symbol = createSymbol(symbolType, -1, -1, 2, 2, color);
  477. symbol.setStyle('strokeNoScale', true);
  478. group.add(symbol);
  479. callback && callback.onCreate(symbol);
  480. } else {
  481. symbol.setColor(color);
  482. group.add(symbol); // Group may be new, also need to add.
  483. callback && callback.onUpdate(symbol);
  484. } // Style
  485. var itemStyle = itemStyleModel.getItemStyle(['color', 'symbol', 'symbolSize']);
  486. symbol.setStyle(itemStyle); // Transform and events.
  487. opt = zrUtil.merge({
  488. rectHover: true,
  489. z2: 100
  490. }, opt, true);
  491. var symbolSize = hostModel.get('symbolSize');
  492. symbolSize = symbolSize instanceof Array ? symbolSize.slice() : [+symbolSize, +symbolSize];
  493. symbolSize[0] /= 2;
  494. symbolSize[1] /= 2;
  495. opt.scale = symbolSize;
  496. var symbolOffset = hostModel.get('symbolOffset');
  497. if (symbolOffset) {
  498. var pos = opt.position = opt.position || [0, 0];
  499. pos[0] += numberUtil.parsePercent(symbolOffset[0], symbolSize[0]);
  500. pos[1] += numberUtil.parsePercent(symbolOffset[1], symbolSize[1]);
  501. }
  502. var symbolRotate = hostModel.get('symbolRotate');
  503. opt.rotation = (symbolRotate || 0) * Math.PI / 180 || 0;
  504. symbol.attr(opt); // FIXME
  505. // (1) When symbol.style.strokeNoScale is true and updateTransform is not performed,
  506. // getBoundingRect will return wrong result.
  507. // (This is supposed to be resolved in zrender, but it is a little difficult to
  508. // leverage performance and auto updateTransform)
  509. // (2) All of ancesters of symbol do not scale, so we can just updateTransform symbol.
  510. symbol.updateTransform();
  511. return symbol;
  512. }
  513. function pointerMoveTo(pointer, dataIndex, axis, timelineModel, noAnimation) {
  514. if (pointer.dragging) {
  515. return;
  516. }
  517. var pointerModel = timelineModel.getModel('checkpointStyle');
  518. var toCoord = axis.dataToCoord(timelineModel.getData().get(['value'], dataIndex));
  519. if (noAnimation || !pointerModel.get('animation', true)) {
  520. pointer.attr({
  521. position: [toCoord, 0]
  522. });
  523. } else {
  524. pointer.stopAnimation(true);
  525. pointer.animateTo({
  526. position: [toCoord, 0]
  527. }, pointerModel.get('animationDuration', true), pointerModel.get('animationEasing', true));
  528. }
  529. }
  530. module.exports = _default;