detail.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. /**
  2. * Handle the detail animations
  3. * @author sima.zhang1990@gmail.com
  4. */
  5. import { mix, isArray, isNumber, each, isNil, isFunction, isString, isObject, deepMix } from '../util/common';
  6. import Element from '../graphic/engine/element';
  7. import Timeline from '../graphic/animate/timeline';
  8. import Animator from '../graphic/animate/animator';
  9. import Animate from './animate';
  10. import * as ShapeAction from './shape-action';
  11. import * as GroupAction from './group-action';
  12. import Chart from '../chart/chart';
  13. var timeline;
  14. Element.prototype.animate = function () {
  15. var attrs = mix({}, this.get('attrs'));
  16. return new Animator(this, attrs, timeline);
  17. };
  18. Chart.prototype.animate = function (cfg) {
  19. this.set('animate', cfg);
  20. return this;
  21. };
  22. Animate.Action = ShapeAction;
  23. Animate.defaultCfg = {
  24. interval: {
  25. enter(coord) {
  26. if (coord.isPolar && coord.transposed) {
  27. // for pie chart
  28. return function (shape) {
  29. shape.set('zIndex', -1);
  30. var container = shape.get('parent');
  31. container.sort();
  32. };
  33. }
  34. return ShapeAction.fadeIn;
  35. }
  36. },
  37. area: {
  38. enter(coord) {
  39. if (coord.isPolar) return null;
  40. return ShapeAction.fadeIn;
  41. }
  42. },
  43. line: {
  44. enter(coord) {
  45. if (coord.isPolar) return null;
  46. return ShapeAction.fadeIn;
  47. }
  48. },
  49. path: {
  50. enter(coord) {
  51. if (coord.isPolar) return null;
  52. return ShapeAction.fadeIn;
  53. }
  54. }
  55. };
  56. var GROUP_ANIMATION = {
  57. line(coord) {
  58. if (coord.isPolar) {
  59. return GroupAction.groupScaleInXY;
  60. }
  61. return GroupAction.groupWaveIn;
  62. },
  63. area(coord) {
  64. if (coord.isPolar) {
  65. return GroupAction.groupScaleInXY;
  66. }
  67. return GroupAction.groupWaveIn;
  68. },
  69. path(coord) {
  70. if (coord.isPolar) {
  71. return GroupAction.groupScaleInXY;
  72. }
  73. return GroupAction.groupWaveIn;
  74. },
  75. point() {
  76. return GroupAction.shapesScaleInXY;
  77. },
  78. interval(coord) {
  79. var result;
  80. if (coord.isPolar) {
  81. // polar coodinate
  82. result = GroupAction.groupScaleInXY;
  83. if (coord.transposed) {
  84. // pie chart
  85. result = GroupAction.groupWaveIn;
  86. }
  87. } else {
  88. result = coord.transposed ? GroupAction.groupScaleInX : GroupAction.groupScaleInY;
  89. }
  90. return result;
  91. },
  92. schema() {
  93. return GroupAction.groupWaveIn;
  94. }
  95. };
  96. function diff(fromAttrs, toAttrs) {
  97. var endState = {};
  98. for (var k in toAttrs) {
  99. if (isNumber(fromAttrs[k]) && fromAttrs[k] !== toAttrs[k]) {
  100. endState[k] = toAttrs[k];
  101. } else if (isArray(fromAttrs[k]) && JSON.stringify(fromAttrs[k]) !== JSON.stringify(toAttrs[k])) {
  102. endState[k] = toAttrs[k];
  103. }
  104. }
  105. return endState;
  106. } // Add a unique id identifier to each shape
  107. function _getShapeId(geom, dataObj, geomIdx) {
  108. var type = geom.get('type');
  109. var id = 'geom' + geomIdx + '-' + type;
  110. var xScale = geom.getXScale();
  111. var yScale = geom.getYScale();
  112. var xField = xScale.field || 'x';
  113. var yField = yScale.field || 'y';
  114. var yVal = dataObj[yField];
  115. var xVal;
  116. if (xScale.isIdentity) {
  117. xVal = xScale.value;
  118. } else {
  119. xVal = dataObj[xField];
  120. }
  121. if (type === 'interval' || type === 'schema') {
  122. id += '-' + xVal;
  123. } else if (type === 'line' || type === 'area' || type === 'path') {
  124. id += '-' + type;
  125. } else {
  126. id += xScale.isCategory ? '-' + xVal : '-' + xVal + '-' + yVal;
  127. }
  128. var groupScales = geom._getGroupScales();
  129. each(groupScales, function (groupScale) {
  130. var field = groupScale.field;
  131. if (groupScale.type !== 'identity') {
  132. id += '-' + dataObj[field];
  133. }
  134. });
  135. return id;
  136. } // get geometry's shapes
  137. function getShapes(geoms, chart, coord) {
  138. var shapes = [];
  139. each(geoms, function (geom, geomIdx) {
  140. var geomContainer = geom.get('container');
  141. var geomShapes = geomContainer.get('children');
  142. var type = geom.get('type');
  143. var animateCfg = isNil(geom.get('animateCfg')) ? _getAnimateCfgByShapeType(type, chart) : geom.get('animateCfg');
  144. if (animateCfg !== false) {
  145. each(geomShapes, function (shape, index) {
  146. if (shape.get('className') === type) {
  147. shape._id = _getShapeId(geom, shape.get('origin')._origin, geomIdx);
  148. shape.set('coord', coord);
  149. shape.set('animateCfg', animateCfg);
  150. shape.set('index', index);
  151. shapes.push(shape);
  152. }
  153. });
  154. }
  155. geom.set('shapes', geomShapes);
  156. });
  157. return shapes;
  158. }
  159. function cache(shapes) {
  160. var rst = {};
  161. for (var i = 0, len = shapes.length; i < len; i++) {
  162. var shape = shapes[i];
  163. if (!shape._id || shape.isClip) continue;
  164. var id = shape._id;
  165. rst[id] = {
  166. _id: id,
  167. type: shape.get('type'),
  168. // the type of shape
  169. attrs: mix({}, shape._attrs.attrs),
  170. // the graphics attributes of shape
  171. className: shape.get('className'),
  172. geomType: shape.get('className'),
  173. index: shape.get('index'),
  174. coord: shape.get('coord'),
  175. animateCfg: shape.get('animateCfg')
  176. };
  177. }
  178. return rst;
  179. }
  180. function getAnimate(geomType, coord, animationType, animationName) {
  181. var result;
  182. if (isFunction(animationName)) {
  183. result = animationName;
  184. } else if (isString(animationName)) {
  185. result = Animate.Action[animationName];
  186. } else {
  187. result = Animate.getAnimation(geomType, coord, animationType);
  188. }
  189. return result;
  190. }
  191. function getAnimateCfg(geomType, animationType, animateCfg) {
  192. if (animateCfg === false || isObject(animateCfg) && animateCfg[animationType] === false) {
  193. return false;
  194. }
  195. var defaultCfg = Animate.getAnimateCfg(geomType, animationType);
  196. if (animateCfg && animateCfg[animationType]) {
  197. return deepMix({}, defaultCfg, animateCfg[animationType]);
  198. }
  199. return defaultCfg;
  200. }
  201. function addAnimate(cache, shapes, canvas) {
  202. var animate;
  203. var animateCfg; // the order of animation: leave -> update -> enter
  204. var updateShapes = [];
  205. var newShapes = [];
  206. each(shapes, function (shape) {
  207. var result = cache[shape._id];
  208. if (!result) {
  209. newShapes.push(shape);
  210. } else {
  211. shape.set('cacheShape', result);
  212. updateShapes.push(shape);
  213. delete cache[shape._id];
  214. }
  215. }); // first do the leave animation
  216. each(cache, function (deletedShape) {
  217. var {
  218. className,
  219. coord,
  220. _id,
  221. attrs,
  222. index,
  223. type
  224. } = deletedShape;
  225. animateCfg = getAnimateCfg(className, 'leave', deletedShape.animateCfg);
  226. if (animateCfg === false) return true;
  227. animate = getAnimate(className, coord, 'leave', animateCfg.animation);
  228. if (isFunction(animate)) {
  229. var tempShape = canvas.addShape(type, {
  230. attrs,
  231. index,
  232. canvas,
  233. className
  234. });
  235. tempShape._id = _id;
  236. animate(tempShape, animateCfg, coord);
  237. }
  238. }); // then do the update animation
  239. each(updateShapes, function (updateShape) {
  240. var className = updateShape.get('className');
  241. animateCfg = getAnimateCfg(className, 'update', updateShape.get('animateCfg'));
  242. if (animateCfg === false) return true;
  243. var coord = updateShape.get('coord');
  244. var cacheAttrs = updateShape.get('cacheShape').attrs;
  245. var endState = diff(cacheAttrs, updateShape._attrs.attrs); // 判断如果属性相同的话就不进行变换
  246. if (Object.keys(endState).length) {
  247. animate = getAnimate(className, coord, 'update', animateCfg.animation);
  248. if (isFunction(animate)) {
  249. animate(updateShape, animateCfg, coord);
  250. } else {
  251. var startState = {};
  252. each(endState, function (value, key) {
  253. startState[key] = cacheAttrs[key];
  254. });
  255. updateShape.attr(startState);
  256. updateShape.animate().to({
  257. attrs: endState,
  258. duration: animateCfg.duration,
  259. easing: animateCfg.easing,
  260. delay: animateCfg.delay
  261. }).onEnd(function () {
  262. updateShape.set('cacheShape', null);
  263. });
  264. }
  265. }
  266. }); // last, enter animation
  267. each(newShapes, function (newShape) {
  268. // 新图形元素的进场元素
  269. var className = newShape.get('className');
  270. var coord = newShape.get('coord');
  271. animateCfg = getAnimateCfg(className, 'enter', newShape.get('animateCfg'));
  272. if (animateCfg === false) return true;
  273. animate = getAnimate(className, coord, 'enter', animateCfg.animation);
  274. if (isFunction(animate)) {
  275. if (className === 'interval' && coord.isPolar && coord.transposed) {
  276. var index = newShape.get('index');
  277. var lastShape = updateShapes[index - 1];
  278. animate(newShape, animateCfg, lastShape);
  279. } else {
  280. animate(newShape, animateCfg, coord);
  281. }
  282. }
  283. });
  284. }
  285. function _getAnimateCfgByShapeType(type, chart) {
  286. if (!type) {
  287. return null;
  288. }
  289. var animateCfg = chart.get('animate');
  290. if (type.indexOf('guide-tag') > -1) {
  291. type = 'guide-tag';
  292. }
  293. if (isObject(animateCfg)) {
  294. return animateCfg[type];
  295. }
  296. if (animateCfg === false) {
  297. return false;
  298. }
  299. return null;
  300. }
  301. function afterCanvasInit()
  302. /* chart */
  303. {
  304. timeline = new Timeline();
  305. timeline.play();
  306. }
  307. function beforeCanvasDraw(chart) {
  308. if (chart.get('animate') === false) {
  309. return;
  310. }
  311. var isUpdate = chart.get('isUpdate');
  312. var canvas = chart.get('canvas');
  313. var coord = chart.get('coord');
  314. var geoms = chart.get('geoms');
  315. var caches = canvas.get('caches') || [];
  316. if (caches.length === 0) {
  317. isUpdate = false;
  318. }
  319. var cacheShapes = getShapes(geoms, chart, coord);
  320. var {
  321. frontPlot,
  322. backPlot
  323. } = chart.get('axisController');
  324. var axisShapes = frontPlot.get('children').concat(backPlot.get('children'));
  325. var guideShapes = [];
  326. if (chart.get('guideController')) {
  327. guideShapes = chart.get('guideController').guideShapes;
  328. }
  329. var componentShapes = [];
  330. axisShapes.concat(guideShapes).forEach(function (s) {
  331. var className = s.get('className');
  332. var animateCfg = _getAnimateCfgByShapeType(className, chart);
  333. s.set('coord', coord);
  334. s.set('animateCfg', animateCfg);
  335. componentShapes.push(s);
  336. cacheShapes.push(s);
  337. });
  338. canvas.set('caches', cache(cacheShapes));
  339. if (isUpdate) {
  340. addAnimate(caches, cacheShapes, canvas);
  341. } else {
  342. // do the appear animation
  343. var animateCfg;
  344. var animate;
  345. each(geoms, function (geom) {
  346. var type = geom.get('type');
  347. var geomCfg = isNil(geom.get('animateCfg')) ? _getAnimateCfgByShapeType(type, chart) : geom.get('animateCfg');
  348. if (geomCfg !== false) {
  349. animateCfg = getAnimateCfg(type, 'appear', geomCfg);
  350. animate = getAnimate(type, coord, 'appear', animateCfg.animation);
  351. if (isFunction(animate)) {
  352. var shapes = geom.get('shapes');
  353. each(shapes, function (shape) {
  354. animate(shape, animateCfg, coord);
  355. });
  356. } else if (GROUP_ANIMATION[type]) {
  357. // do the default animation
  358. animate = GroupAction[animateCfg.animation] || GROUP_ANIMATION[type](coord);
  359. var yScale = geom.getYScale();
  360. var zeroY = coord.convertPoint({
  361. x: 0,
  362. y: yScale.scale(geom.getYMinValue())
  363. });
  364. var container = geom.get('container');
  365. animate && animate(container, animateCfg, coord, zeroY);
  366. }
  367. }
  368. }); // do the animation of components
  369. each(componentShapes, function (shape) {
  370. var animateCfg = shape.get('animateCfg');
  371. var className = shape.get('className');
  372. if (animateCfg && animateCfg.appear) {
  373. // if user configure
  374. var defaultCfg = Animate.getAnimateCfg(className, 'appear');
  375. var appearCfg = deepMix({}, defaultCfg, animateCfg.appear);
  376. var _animate = getAnimate(className, coord, 'appear', appearCfg.animation);
  377. if (isFunction(_animate)) {
  378. _animate(shape, appearCfg, coord);
  379. }
  380. }
  381. });
  382. }
  383. }
  384. function afterCanvasDestroyed()
  385. /* chart */
  386. {
  387. timeline.stop();
  388. }
  389. export { afterCanvasInit, beforeCanvasDraw, afterCanvasDestroyed };
  390. export default {
  391. afterCanvasInit,
  392. beforeCanvasDraw,
  393. afterCanvasDestroyed
  394. };