universalTransition.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  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. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. // Universal transitions that can animate between any shapes(series) and any properties in any amounts.
  41. import { SERIES_UNIVERSAL_TRANSITION_PROP } from '../model/Series.js';
  42. import { createHashMap, each, map, filter, isArray, extend } from 'zrender/lib/core/util.js';
  43. import { applyMorphAnimation, getPathList } from './morphTransitionHelper.js';
  44. import Path from 'zrender/lib/graphic/Path.js';
  45. import { initProps } from '../util/graphic.js';
  46. import DataDiffer from '../data/DataDiffer.js';
  47. import { makeInner, normalizeToArray } from '../util/model.js';
  48. import { warn } from '../util/log.js';
  49. import { getAnimationConfig, getOldStyle } from './basicTransition.js';
  50. import Displayable from 'zrender/lib/graphic/Displayable.js';
  51. var DATA_COUNT_THRESHOLD = 1e4;
  52. var TRANSITION_NONE = 0;
  53. var TRANSITION_P2C = 1;
  54. var TRANSITION_C2P = 2;
  55. ;
  56. var getUniversalTransitionGlobalStore = makeInner();
  57. function getDimension(data, visualDimension) {
  58. var dimensions = data.dimensions;
  59. for (var i = 0; i < dimensions.length; i++) {
  60. var dimInfo = data.getDimensionInfo(dimensions[i]);
  61. if (dimInfo && dimInfo.otherDims[visualDimension] === 0) {
  62. return dimensions[i];
  63. }
  64. }
  65. }
  66. // get value by dimension. (only get value of itemGroupId or childGroupId, so convert it to string)
  67. function getValueByDimension(data, dataIndex, dimension) {
  68. var dimInfo = data.getDimensionInfo(dimension);
  69. var dimOrdinalMeta = dimInfo && dimInfo.ordinalMeta;
  70. if (dimInfo) {
  71. var value = data.get(dimInfo.name, dataIndex);
  72. if (dimOrdinalMeta) {
  73. return dimOrdinalMeta.categories[value] || value + '';
  74. }
  75. return value + '';
  76. }
  77. }
  78. function getGroupId(data, dataIndex, dataGroupId, isChild) {
  79. // try to get groupId from encode
  80. var visualDimension = isChild ? 'itemChildGroupId' : 'itemGroupId';
  81. var groupIdDim = getDimension(data, visualDimension);
  82. if (groupIdDim) {
  83. var groupId = getValueByDimension(data, dataIndex, groupIdDim);
  84. return groupId;
  85. }
  86. // try to get groupId from raw data item
  87. var rawDataItem = data.getRawDataItem(dataIndex);
  88. var property = isChild ? 'childGroupId' : 'groupId';
  89. if (rawDataItem && rawDataItem[property]) {
  90. return rawDataItem[property] + '';
  91. }
  92. // fallback
  93. if (isChild) {
  94. return;
  95. }
  96. // try to use series.dataGroupId as groupId, otherwise use dataItem's id as groupId
  97. return dataGroupId || data.getId(dataIndex);
  98. }
  99. // flatten all data items from different serieses into one arrary
  100. function flattenDataDiffItems(list) {
  101. var items = [];
  102. each(list, function (seriesInfo) {
  103. var data = seriesInfo.data;
  104. var dataGroupId = seriesInfo.dataGroupId;
  105. if (data.count() > DATA_COUNT_THRESHOLD) {
  106. if (process.env.NODE_ENV !== 'production') {
  107. warn('Universal transition is disabled on large data > 10k.');
  108. }
  109. return;
  110. }
  111. var indices = data.getIndices();
  112. for (var dataIndex = 0; dataIndex < indices.length; dataIndex++) {
  113. items.push({
  114. data: data,
  115. groupId: getGroupId(data, dataIndex, dataGroupId, false),
  116. childGroupId: getGroupId(data, dataIndex, dataGroupId, true),
  117. divide: seriesInfo.divide,
  118. dataIndex: dataIndex
  119. });
  120. }
  121. });
  122. return items;
  123. }
  124. function fadeInElement(newEl, newSeries, newIndex) {
  125. newEl.traverse(function (el) {
  126. if (el instanceof Path) {
  127. // TODO use fade in animation for target element.
  128. initProps(el, {
  129. style: {
  130. opacity: 0
  131. }
  132. }, newSeries, {
  133. dataIndex: newIndex,
  134. isFrom: true
  135. });
  136. }
  137. });
  138. }
  139. function removeEl(el) {
  140. if (el.parent) {
  141. // Bake parent transform to element.
  142. // So it can still have proper transform to transition after it's removed.
  143. var computedTransform = el.getComputedTransform();
  144. el.setLocalTransform(computedTransform);
  145. el.parent.remove(el);
  146. }
  147. }
  148. function stopAnimation(el) {
  149. el.stopAnimation();
  150. if (el.isGroup) {
  151. el.traverse(function (child) {
  152. child.stopAnimation();
  153. });
  154. }
  155. }
  156. function animateElementStyles(el, dataIndex, seriesModel) {
  157. var animationConfig = getAnimationConfig('update', seriesModel, dataIndex);
  158. animationConfig && el.traverse(function (child) {
  159. if (child instanceof Displayable) {
  160. var oldStyle = getOldStyle(child);
  161. if (oldStyle) {
  162. child.animateFrom({
  163. style: oldStyle
  164. }, animationConfig);
  165. }
  166. }
  167. });
  168. }
  169. function isAllIdSame(oldDiffItems, newDiffItems) {
  170. var len = oldDiffItems.length;
  171. if (len !== newDiffItems.length) {
  172. return false;
  173. }
  174. for (var i = 0; i < len; i++) {
  175. var oldItem = oldDiffItems[i];
  176. var newItem = newDiffItems[i];
  177. if (oldItem.data.getId(oldItem.dataIndex) !== newItem.data.getId(newItem.dataIndex)) {
  178. return false;
  179. }
  180. }
  181. return true;
  182. }
  183. function transitionBetween(oldList, newList, api) {
  184. var oldDiffItems = flattenDataDiffItems(oldList);
  185. var newDiffItems = flattenDataDiffItems(newList);
  186. function updateMorphingPathProps(from, to, rawFrom, rawTo, animationCfg) {
  187. if (rawFrom || from) {
  188. to.animateFrom({
  189. style: rawFrom && rawFrom !== from
  190. // dividingMethod like clone may override the style(opacity)
  191. // So extend it to raw style.
  192. ? extend(extend({}, rawFrom.style), from.style) : from.style
  193. }, animationCfg);
  194. }
  195. }
  196. var hasMorphAnimation = false;
  197. /**
  198. * With groupId and childGroupId, we can build parent-child relationships between dataItems.
  199. * However, we should mind the parent-child "direction" between old and new options.
  200. *
  201. * For example, suppose we have two dataItems from two series.data:
  202. *
  203. * dataA: [ dataB: [
  204. * { {
  205. * value: 5, value: 3,
  206. * groupId: 'creatures', groupId: 'animals',
  207. * childGroupId: 'animals' childGroupId: 'dogs'
  208. * }, },
  209. * ... ...
  210. * ] ]
  211. *
  212. * where dataA is belong to optionA and dataB is belong to optionB.
  213. *
  214. * When we `setOption(optionB)` from optionA, we choose childGroupId of dataItemA and groupId of
  215. * dataItemB as keys so the two keys are matched (both are 'animals'), then universalTransition
  216. * will work. This derection is "parent -> child".
  217. *
  218. * If we `setOption(optionA)` from optionB, we also choose groupId of dataItemB and childGroupId
  219. * of dataItemA as keys and universalTransition will work. This derection is "child -> parent".
  220. *
  221. * If there is no childGroupId specified, which means no multiLevelDrillDown/Up is needed and no
  222. * parent-child relationship exists. This direction is "none".
  223. *
  224. * So we need to know whether to use groupId or childGroupId as the key when we call the keyGetter
  225. * functions. Thus, we need to decide the direction first.
  226. *
  227. * The rule is:
  228. *
  229. * if (all childGroupIds in oldDiffItems and all groupIds in newDiffItems have common value) {
  230. * direction = 'parent -> child';
  231. * } else if (all groupIds in oldDiffItems and all childGroupIds in newDiffItems have common value) {
  232. * direction = 'child -> parent';
  233. * } else {
  234. * direction = 'none';
  235. * }
  236. */
  237. var direction = TRANSITION_NONE;
  238. // find all groupIds and childGroupIds from oldDiffItems
  239. var oldGroupIds = createHashMap();
  240. var oldChildGroupIds = createHashMap();
  241. oldDiffItems.forEach(function (item) {
  242. item.groupId && oldGroupIds.set(item.groupId, true);
  243. item.childGroupId && oldChildGroupIds.set(item.childGroupId, true);
  244. });
  245. // traverse newDiffItems and decide the direction according to the rule
  246. for (var i = 0; i < newDiffItems.length; i++) {
  247. var newGroupId = newDiffItems[i].groupId;
  248. if (oldChildGroupIds.get(newGroupId)) {
  249. direction = TRANSITION_P2C;
  250. break;
  251. }
  252. var newChildGroupId = newDiffItems[i].childGroupId;
  253. if (newChildGroupId && oldGroupIds.get(newChildGroupId)) {
  254. direction = TRANSITION_C2P;
  255. break;
  256. }
  257. }
  258. function createKeyGetter(isOld, onlyGetId) {
  259. return function (diffItem) {
  260. var data = diffItem.data;
  261. var dataIndex = diffItem.dataIndex;
  262. // TODO if specified dim
  263. if (onlyGetId) {
  264. return data.getId(dataIndex);
  265. }
  266. if (isOld) {
  267. return direction === TRANSITION_P2C ? diffItem.childGroupId : diffItem.groupId;
  268. } else {
  269. return direction === TRANSITION_C2P ? diffItem.childGroupId : diffItem.groupId;
  270. }
  271. };
  272. }
  273. // Use id if it's very likely to be an one to one animation
  274. // It's more robust than groupId
  275. // TODO Check if key dimension is specified.
  276. var useId = isAllIdSame(oldDiffItems, newDiffItems);
  277. var isElementStillInChart = {};
  278. if (!useId) {
  279. // We may have different diff strategy with basicTransition if we use other dimension as key.
  280. // If so, we can't simply check if oldEl is same with newEl. We need a map to check if oldEl is still being used in the new chart.
  281. // We can't use the elements that already being morphed. Let it keep it's original basic transition.
  282. for (var i = 0; i < newDiffItems.length; i++) {
  283. var newItem = newDiffItems[i];
  284. var el = newItem.data.getItemGraphicEl(newItem.dataIndex);
  285. if (el) {
  286. isElementStillInChart[el.id] = true;
  287. }
  288. }
  289. }
  290. function updateOneToOne(newIndex, oldIndex) {
  291. var oldItem = oldDiffItems[oldIndex];
  292. var newItem = newDiffItems[newIndex];
  293. var newSeries = newItem.data.hostModel;
  294. // TODO Mark this elements is morphed and don't morph them anymore
  295. var oldEl = oldItem.data.getItemGraphicEl(oldItem.dataIndex);
  296. var newEl = newItem.data.getItemGraphicEl(newItem.dataIndex);
  297. // Can't handle same elements.
  298. if (oldEl === newEl) {
  299. newEl && animateElementStyles(newEl, newItem.dataIndex, newSeries);
  300. return;
  301. }
  302. if (
  303. // We can't use the elements that already being morphed
  304. oldEl && isElementStillInChart[oldEl.id]) {
  305. return;
  306. }
  307. if (newEl) {
  308. // TODO: If keep animating the group in case
  309. // some of the elements don't want to be morphed.
  310. // TODO Label?
  311. stopAnimation(newEl);
  312. if (oldEl) {
  313. stopAnimation(oldEl);
  314. // If old element is doing leaving animation. stop it and remove it immediately.
  315. removeEl(oldEl);
  316. hasMorphAnimation = true;
  317. applyMorphAnimation(getPathList(oldEl), getPathList(newEl), newItem.divide, newSeries, newIndex, updateMorphingPathProps);
  318. } else {
  319. fadeInElement(newEl, newSeries, newIndex);
  320. }
  321. }
  322. // else keep oldEl leaving animation.
  323. }
  324. new DataDiffer(oldDiffItems, newDiffItems, createKeyGetter(true, useId), createKeyGetter(false, useId), null, 'multiple').update(updateOneToOne).updateManyToOne(function (newIndex, oldIndices) {
  325. var newItem = newDiffItems[newIndex];
  326. var newData = newItem.data;
  327. var newSeries = newData.hostModel;
  328. var newEl = newData.getItemGraphicEl(newItem.dataIndex);
  329. var oldElsList = filter(map(oldIndices, function (idx) {
  330. return oldDiffItems[idx].data.getItemGraphicEl(oldDiffItems[idx].dataIndex);
  331. }), function (oldEl) {
  332. return oldEl && oldEl !== newEl && !isElementStillInChart[oldEl.id];
  333. });
  334. if (newEl) {
  335. stopAnimation(newEl);
  336. if (oldElsList.length) {
  337. // If old element is doing leaving animation. stop it and remove it immediately.
  338. each(oldElsList, function (oldEl) {
  339. stopAnimation(oldEl);
  340. removeEl(oldEl);
  341. });
  342. hasMorphAnimation = true;
  343. applyMorphAnimation(getPathList(oldElsList), getPathList(newEl), newItem.divide, newSeries, newIndex, updateMorphingPathProps);
  344. } else {
  345. fadeInElement(newEl, newSeries, newItem.dataIndex);
  346. }
  347. }
  348. // else keep oldEl leaving animation.
  349. }).updateOneToMany(function (newIndices, oldIndex) {
  350. var oldItem = oldDiffItems[oldIndex];
  351. var oldEl = oldItem.data.getItemGraphicEl(oldItem.dataIndex);
  352. // We can't use the elements that already being morphed
  353. if (oldEl && isElementStillInChart[oldEl.id]) {
  354. return;
  355. }
  356. var newElsList = filter(map(newIndices, function (idx) {
  357. return newDiffItems[idx].data.getItemGraphicEl(newDiffItems[idx].dataIndex);
  358. }), function (el) {
  359. return el && el !== oldEl;
  360. });
  361. var newSeris = newDiffItems[newIndices[0]].data.hostModel;
  362. if (newElsList.length) {
  363. each(newElsList, function (newEl) {
  364. return stopAnimation(newEl);
  365. });
  366. if (oldEl) {
  367. stopAnimation(oldEl);
  368. // If old element is doing leaving animation. stop it and remove it immediately.
  369. removeEl(oldEl);
  370. hasMorphAnimation = true;
  371. applyMorphAnimation(getPathList(oldEl), getPathList(newElsList), oldItem.divide,
  372. // Use divide on old.
  373. newSeris, newIndices[0], updateMorphingPathProps);
  374. } else {
  375. each(newElsList, function (newEl) {
  376. return fadeInElement(newEl, newSeris, newIndices[0]);
  377. });
  378. }
  379. }
  380. // else keep oldEl leaving animation.
  381. }).updateManyToMany(function (newIndices, oldIndices) {
  382. // If two data are same and both have groupId.
  383. // Normally they should be diff by id.
  384. new DataDiffer(oldIndices, newIndices, function (rawIdx) {
  385. return oldDiffItems[rawIdx].data.getId(oldDiffItems[rawIdx].dataIndex);
  386. }, function (rawIdx) {
  387. return newDiffItems[rawIdx].data.getId(newDiffItems[rawIdx].dataIndex);
  388. }).update(function (newIndex, oldIndex) {
  389. // Use the original index
  390. updateOneToOne(newIndices[newIndex], oldIndices[oldIndex]);
  391. }).execute();
  392. }).execute();
  393. if (hasMorphAnimation) {
  394. each(newList, function (_a) {
  395. var data = _a.data;
  396. var seriesModel = data.hostModel;
  397. var view = seriesModel && api.getViewOfSeriesModel(seriesModel);
  398. var animationCfg = getAnimationConfig('update', seriesModel, 0); // use 0 index.
  399. if (view && seriesModel.isAnimationEnabled() && animationCfg && animationCfg.duration > 0) {
  400. view.group.traverse(function (el) {
  401. if (el instanceof Path && !el.animators.length) {
  402. // We can't accept there still exists element that has no animation
  403. // if universalTransition is enabled
  404. el.animateFrom({
  405. style: {
  406. opacity: 0
  407. }
  408. }, animationCfg);
  409. }
  410. });
  411. }
  412. });
  413. }
  414. }
  415. function getSeriesTransitionKey(series) {
  416. var seriesKey = series.getModel('universalTransition').get('seriesKey');
  417. if (!seriesKey) {
  418. // Use series id by default.
  419. return series.id;
  420. }
  421. return seriesKey;
  422. }
  423. function convertArraySeriesKeyToString(seriesKey) {
  424. if (isArray(seriesKey)) {
  425. // Order independent.
  426. return seriesKey.sort().join(',');
  427. }
  428. return seriesKey;
  429. }
  430. function getDivideShapeFromData(data) {
  431. if (data.hostModel) {
  432. return data.hostModel.getModel('universalTransition').get('divideShape');
  433. }
  434. }
  435. function findTransitionSeriesBatches(globalStore, params) {
  436. var updateBatches = createHashMap();
  437. var oldDataMap = createHashMap();
  438. // Map that only store key in array seriesKey.
  439. // Which is used to query the old data when transition from one to multiple series.
  440. var oldDataMapForSplit = createHashMap();
  441. each(globalStore.oldSeries, function (series, idx) {
  442. var oldDataGroupId = globalStore.oldDataGroupIds[idx];
  443. var oldData = globalStore.oldData[idx];
  444. var transitionKey = getSeriesTransitionKey(series);
  445. var transitionKeyStr = convertArraySeriesKeyToString(transitionKey);
  446. oldDataMap.set(transitionKeyStr, {
  447. dataGroupId: oldDataGroupId,
  448. data: oldData
  449. });
  450. if (isArray(transitionKey)) {
  451. // Same key can't in different array seriesKey.
  452. each(transitionKey, function (key) {
  453. oldDataMapForSplit.set(key, {
  454. key: transitionKeyStr,
  455. dataGroupId: oldDataGroupId,
  456. data: oldData
  457. });
  458. });
  459. }
  460. });
  461. function checkTransitionSeriesKeyDuplicated(transitionKeyStr) {
  462. if (updateBatches.get(transitionKeyStr)) {
  463. warn("Duplicated seriesKey in universalTransition " + transitionKeyStr);
  464. }
  465. }
  466. each(params.updatedSeries, function (series) {
  467. if (series.isUniversalTransitionEnabled() && series.isAnimationEnabled()) {
  468. var newDataGroupId = series.get('dataGroupId');
  469. var newData = series.getData();
  470. var transitionKey = getSeriesTransitionKey(series);
  471. var transitionKeyStr = convertArraySeriesKeyToString(transitionKey);
  472. // Only transition between series with same id.
  473. var oldData = oldDataMap.get(transitionKeyStr);
  474. // string transition key is the best match.
  475. if (oldData) {
  476. if (process.env.NODE_ENV !== 'production') {
  477. checkTransitionSeriesKeyDuplicated(transitionKeyStr);
  478. }
  479. // TODO check if data is same?
  480. updateBatches.set(transitionKeyStr, {
  481. oldSeries: [{
  482. dataGroupId: oldData.dataGroupId,
  483. divide: getDivideShapeFromData(oldData.data),
  484. data: oldData.data
  485. }],
  486. newSeries: [{
  487. dataGroupId: newDataGroupId,
  488. divide: getDivideShapeFromData(newData),
  489. data: newData
  490. }]
  491. });
  492. } else {
  493. // Transition from multiple series.
  494. // e.g. 'female', 'male' -> ['female', 'male']
  495. if (isArray(transitionKey)) {
  496. if (process.env.NODE_ENV !== 'production') {
  497. checkTransitionSeriesKeyDuplicated(transitionKeyStr);
  498. }
  499. var oldSeries_1 = [];
  500. each(transitionKey, function (key) {
  501. var oldData = oldDataMap.get(key);
  502. if (oldData.data) {
  503. oldSeries_1.push({
  504. dataGroupId: oldData.dataGroupId,
  505. divide: getDivideShapeFromData(oldData.data),
  506. data: oldData.data
  507. });
  508. }
  509. });
  510. if (oldSeries_1.length) {
  511. updateBatches.set(transitionKeyStr, {
  512. oldSeries: oldSeries_1,
  513. newSeries: [{
  514. dataGroupId: newDataGroupId,
  515. data: newData,
  516. divide: getDivideShapeFromData(newData)
  517. }]
  518. });
  519. }
  520. } else {
  521. // Try transition to multiple series.
  522. // e.g. ['female', 'male'] -> 'female', 'male'
  523. var oldData_1 = oldDataMapForSplit.get(transitionKey);
  524. if (oldData_1) {
  525. var batch = updateBatches.get(oldData_1.key);
  526. if (!batch) {
  527. batch = {
  528. oldSeries: [{
  529. dataGroupId: oldData_1.dataGroupId,
  530. data: oldData_1.data,
  531. divide: getDivideShapeFromData(oldData_1.data)
  532. }],
  533. newSeries: []
  534. };
  535. updateBatches.set(oldData_1.key, batch);
  536. }
  537. batch.newSeries.push({
  538. dataGroupId: newDataGroupId,
  539. data: newData,
  540. divide: getDivideShapeFromData(newData)
  541. });
  542. }
  543. }
  544. }
  545. }
  546. });
  547. return updateBatches;
  548. }
  549. function querySeries(series, finder) {
  550. for (var i = 0; i < series.length; i++) {
  551. var found = finder.seriesIndex != null && finder.seriesIndex === series[i].seriesIndex || finder.seriesId != null && finder.seriesId === series[i].id;
  552. if (found) {
  553. return i;
  554. }
  555. }
  556. }
  557. function transitionSeriesFromOpt(transitionOpt, globalStore, params, api) {
  558. var from = [];
  559. var to = [];
  560. each(normalizeToArray(transitionOpt.from), function (finder) {
  561. var idx = querySeries(globalStore.oldSeries, finder);
  562. if (idx >= 0) {
  563. from.push({
  564. dataGroupId: globalStore.oldDataGroupIds[idx],
  565. data: globalStore.oldData[idx],
  566. // TODO can specify divideShape in transition.
  567. divide: getDivideShapeFromData(globalStore.oldData[idx]),
  568. groupIdDim: finder.dimension
  569. });
  570. }
  571. });
  572. each(normalizeToArray(transitionOpt.to), function (finder) {
  573. var idx = querySeries(params.updatedSeries, finder);
  574. if (idx >= 0) {
  575. var data = params.updatedSeries[idx].getData();
  576. to.push({
  577. dataGroupId: globalStore.oldDataGroupIds[idx],
  578. data: data,
  579. divide: getDivideShapeFromData(data),
  580. groupIdDim: finder.dimension
  581. });
  582. }
  583. });
  584. if (from.length > 0 && to.length > 0) {
  585. transitionBetween(from, to, api);
  586. }
  587. }
  588. export function installUniversalTransition(registers) {
  589. registers.registerUpdateLifecycle('series:beforeupdate', function (ecMOdel, api, params) {
  590. each(normalizeToArray(params.seriesTransition), function (transOpt) {
  591. each(normalizeToArray(transOpt.to), function (finder) {
  592. var series = params.updatedSeries;
  593. for (var i = 0; i < series.length; i++) {
  594. if (finder.seriesIndex != null && finder.seriesIndex === series[i].seriesIndex || finder.seriesId != null && finder.seriesId === series[i].id) {
  595. series[i][SERIES_UNIVERSAL_TRANSITION_PROP] = true;
  596. }
  597. }
  598. });
  599. });
  600. });
  601. registers.registerUpdateLifecycle('series:transition', function (ecModel, api, params) {
  602. // TODO api provide an namespace that can save stuff per instance
  603. var globalStore = getUniversalTransitionGlobalStore(api);
  604. // TODO multiple to multiple series.
  605. if (globalStore.oldSeries && params.updatedSeries && params.optionChanged) {
  606. // TODO transitionOpt was used in an old implementation and can be removed now
  607. // Use give transition config if its' give;
  608. var transitionOpt = params.seriesTransition;
  609. if (transitionOpt) {
  610. each(normalizeToArray(transitionOpt), function (opt) {
  611. transitionSeriesFromOpt(opt, globalStore, params, api);
  612. });
  613. } else {
  614. // Else guess from series based on transition series key.
  615. var updateBatches_1 = findTransitionSeriesBatches(globalStore, params);
  616. each(updateBatches_1.keys(), function (key) {
  617. var batch = updateBatches_1.get(key);
  618. transitionBetween(batch.oldSeries, batch.newSeries, api);
  619. });
  620. }
  621. // Reset
  622. each(params.updatedSeries, function (series) {
  623. // Reset;
  624. if (series[SERIES_UNIVERSAL_TRANSITION_PROP]) {
  625. series[SERIES_UNIVERSAL_TRANSITION_PROP] = false;
  626. }
  627. });
  628. }
  629. // Save all series of current update. Not only the updated one.
  630. var allSeries = ecModel.getSeries();
  631. var savedSeries = globalStore.oldSeries = [];
  632. var savedDataGroupIds = globalStore.oldDataGroupIds = [];
  633. var savedData = globalStore.oldData = [];
  634. for (var i = 0; i < allSeries.length; i++) {
  635. var data = allSeries[i].getData();
  636. // Only save the data that can have transition.
  637. // Avoid large data costing too much extra memory
  638. if (data.count() < DATA_COUNT_THRESHOLD) {
  639. savedSeries.push(allSeries[i]);
  640. savedDataGroupIds.push(allSeries[i].get('dataGroupId'));
  641. savedData.push(data);
  642. }
  643. }
  644. });
  645. }