detail.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /**
  2. * Handle the detail animations
  3. * @author sima.zhang1990@gmail.com
  4. */
  5. var Util = require('../util/common');
  6. var Element = require('../graphic/element');
  7. var Timeline = require('../graphic/animate/timeline');
  8. var Animator = require('../graphic/animate/animator');
  9. var Animate = require('./animate');
  10. var ShapeAction = require('./shape-action');
  11. var GroupAction = require('./group-action');
  12. var Chart = require('../chart/chart');
  13. var timeline;
  14. Element.prototype.animate = function () {
  15. var attrs = Util.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: function 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: function enter(coord) {
  39. if (coord.isPolar) return null;
  40. return ShapeAction.fadeIn;
  41. }
  42. },
  43. line: {
  44. enter: function enter(coord) {
  45. if (coord.isPolar) return null;
  46. return ShapeAction.fadeIn;
  47. }
  48. },
  49. path: {
  50. enter: function enter(coord) {
  51. if (coord.isPolar) return null;
  52. return ShapeAction.fadeIn;
  53. }
  54. }
  55. };
  56. var GROUP_ANIMATION = {
  57. line: function line(coord) {
  58. if (coord.isPolar) {
  59. return GroupAction.groupScaleInXY;
  60. }
  61. return GroupAction.groupWaveIn;
  62. },
  63. area: function area(coord) {
  64. if (coord.isPolar) {
  65. return GroupAction.groupScaleInXY;
  66. }
  67. return GroupAction.groupWaveIn;
  68. },
  69. path: function path(coord) {
  70. if (coord.isPolar) {
  71. return GroupAction.groupScaleInXY;
  72. }
  73. return GroupAction.groupWaveIn;
  74. },
  75. point: function point() {
  76. return GroupAction.shapesScaleInXY;
  77. },
  78. interval: function 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: function schema() {
  93. return GroupAction.groupWaveIn;
  94. }
  95. };
  96. function diff(fromAttrs, toAttrs) {
  97. var endState = {};
  98. for (var k in toAttrs) {
  99. if (Util.isNumber(fromAttrs[k]) && fromAttrs[k] !== toAttrs[k]) {
  100. endState[k] = toAttrs[k];
  101. } else if (Util.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. Util.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. Util.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 = Util.isNil(geom.get('animateCfg')) ? _getAnimateCfgByShapeType(type, chart) : geom.get('animateCfg');
  144. if (animateCfg !== false) {
  145. Util.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: Util.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 (Util.isFunction(animationName)) {
  183. result = animationName;
  184. } else if (Util.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 || Util.isObject(animateCfg) && animateCfg[animationType] === false) {
  193. return false;
  194. }
  195. var defaultCfg = Animate.getAnimateCfg(geomType, animationType);
  196. if (animateCfg && animateCfg[animationType]) {
  197. return Util.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. Util.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. Util.each(cache, function (deletedShape) {
  217. var className = deletedShape.className,
  218. coord = deletedShape.coord,
  219. _id = deletedShape._id,
  220. attrs = deletedShape.attrs,
  221. index = deletedShape.index,
  222. type = deletedShape.type;
  223. animateCfg = getAnimateCfg(className, 'leave', deletedShape.animateCfg);
  224. if (animateCfg === false) return true;
  225. animate = getAnimate(className, coord, 'leave', animateCfg.animation);
  226. if (Util.isFunction(animate)) {
  227. var tempShape = canvas.addShape(type, {
  228. attrs: attrs,
  229. index: index,
  230. canvas: canvas,
  231. className: className
  232. });
  233. tempShape._id = _id;
  234. animate(tempShape, animateCfg, coord);
  235. }
  236. }); // then do the update animation
  237. Util.each(updateShapes, function (updateShape) {
  238. var className = updateShape.get('className');
  239. animateCfg = getAnimateCfg(className, 'update', updateShape.get('animateCfg'));
  240. if (animateCfg === false) return true;
  241. var coord = updateShape.get('coord');
  242. var cacheAttrs = updateShape.get('cacheShape').attrs;
  243. var endState = diff(cacheAttrs, updateShape._attrs.attrs); // 判断如果属性相同的话就不进行变换
  244. if (Object.keys(endState).length) {
  245. animate = getAnimate(className, coord, 'update', animateCfg.animation);
  246. if (Util.isFunction(animate)) {
  247. animate(updateShape, animateCfg, coord);
  248. } else {
  249. updateShape.attr(cacheAttrs);
  250. updateShape.animate().to({
  251. attrs: endState,
  252. duration: animateCfg.duration,
  253. easing: animateCfg.easing,
  254. delay: animateCfg.delay
  255. }).onEnd(function () {
  256. updateShape.set('cacheShape', null);
  257. });
  258. }
  259. }
  260. }); // last, enter animation
  261. Util.each(newShapes, function (newShape) {
  262. // 新图形元素的进场元素
  263. var className = newShape.get('className');
  264. var coord = newShape.get('coord');
  265. animateCfg = getAnimateCfg(className, 'enter', newShape.get('animateCfg'));
  266. if (animateCfg === false) return true;
  267. animate = getAnimate(className, coord, 'enter', animateCfg.animation);
  268. if (Util.isFunction(animate)) {
  269. if (className === 'interval' && coord.isPolar && coord.transposed) {
  270. var index = newShape.get('index');
  271. var lastShape = updateShapes[index - 1];
  272. animate(newShape, animateCfg, lastShape);
  273. } else {
  274. animate(newShape, animateCfg, coord);
  275. }
  276. }
  277. });
  278. }
  279. function _getAnimateCfgByShapeType(type, chart) {
  280. if (!type) {
  281. return null;
  282. }
  283. var animateCfg = chart.get('animate');
  284. if (type.indexOf('guide-tag') > -1) {
  285. type = 'guide-tag';
  286. }
  287. if (Util.isObject(animateCfg)) {
  288. return animateCfg[type];
  289. }
  290. if (animateCfg === false) {
  291. return false;
  292. }
  293. return null;
  294. }
  295. module.exports = {
  296. afterCanvasInit: function afterCanvasInit()
  297. /* chart */
  298. {
  299. timeline = new Timeline();
  300. timeline.play();
  301. },
  302. beforeCanvasDraw: function beforeCanvasDraw(chart) {
  303. if (chart.get('animate') === false) {
  304. return;
  305. }
  306. var isUpdate = chart.get('isUpdate');
  307. var canvas = chart.get('canvas');
  308. var coord = chart.get('coord');
  309. var geoms = chart.get('geoms');
  310. var caches = canvas.get('caches') || [];
  311. if (caches.length === 0) {
  312. isUpdate = false;
  313. }
  314. var cacheShapes = getShapes(geoms, chart, coord);
  315. var _chart$get = chart.get('axisController'),
  316. frontPlot = _chart$get.frontPlot,
  317. backPlot = _chart$get.backPlot;
  318. var axisShapes = frontPlot.get('children').concat(backPlot.get('children'));
  319. var guideShapes = [];
  320. if (chart.get('guideController')) {
  321. guideShapes = chart.get('guideController').guideShapes;
  322. }
  323. var componentShapes = [];
  324. axisShapes.concat(guideShapes).forEach(function (s) {
  325. var className = s.get('className');
  326. var animateCfg = _getAnimateCfgByShapeType(className, chart);
  327. s.set('coord', coord);
  328. s.set('animateCfg', animateCfg);
  329. componentShapes.push(s);
  330. cacheShapes.push(s);
  331. });
  332. canvas.set('caches', cache(cacheShapes));
  333. if (isUpdate) {
  334. addAnimate(caches, cacheShapes, canvas);
  335. } else {
  336. // do the appear animation
  337. var animateCfg;
  338. var animate;
  339. Util.each(geoms, function (geom) {
  340. var type = geom.get('type');
  341. var geomCfg = Util.isNil(geom.get('animateCfg')) ? _getAnimateCfgByShapeType(type, chart) : geom.get('animateCfg');
  342. if (geomCfg !== false) {
  343. animateCfg = getAnimateCfg(type, 'appear', geomCfg);
  344. animate = getAnimate(type, coord, 'appear', animateCfg.animation);
  345. if (Util.isFunction(animate)) {
  346. var shapes = geom.get('shapes');
  347. Util.each(shapes, function (shape) {
  348. animate(shape, animateCfg, coord);
  349. });
  350. } else if (GROUP_ANIMATION[type]) {
  351. // do the default animation
  352. animate = GroupAction[animateCfg.animation] || GROUP_ANIMATION[type](coord);
  353. var yScale = geom.getYScale();
  354. var zeroY = coord.convertPoint({
  355. x: 0,
  356. y: yScale.scale(geom.getYMinValue())
  357. });
  358. var container = geom.get('container');
  359. animate && animate(container, animateCfg, coord, zeroY);
  360. }
  361. }
  362. }); // do the animation of components
  363. Util.each(componentShapes, function (shape) {
  364. var animateCfg = shape.get('animateCfg');
  365. var className = shape.get('className');
  366. if (animateCfg && animateCfg.appear) {
  367. // if user configure
  368. var defaultCfg = Animate.getAnimateCfg(className, 'appear');
  369. var appearCfg = Util.deepMix({}, defaultCfg, animateCfg.appear);
  370. var _animate = getAnimate(className, coord, 'appear', appearCfg.animation);
  371. if (Util.isFunction(_animate)) {
  372. _animate(shape, appearCfg, coord);
  373. }
  374. }
  375. });
  376. }
  377. },
  378. afterCanvasDestroyed: function afterCanvasDestroyed()
  379. /* chart */
  380. {
  381. timeline.stop();
  382. }
  383. };