SliderTimelineView.js 21 KB

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