detail.js 12 KB

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