States.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. var zrUtil = require("../core/util");
  2. var Style = require("./Style");
  3. var _vector = require("../core/vector");
  4. var vec2Copy = _vector.copy;
  5. /**
  6. * States machine for managing graphic states
  7. */
  8. /**
  9. * @typedef {Object} IGraphicState
  10. * @property {number} [zlevel]
  11. * @property {number} [z]
  12. * @property {Array.<number>} {position}
  13. * @property {Array.<number>|number} {rotation}
  14. * @property {Array.<number>} {scale}
  15. * @property {Object} style
  16. *
  17. * @property {Function} onenter
  18. * @property {Function} onleave
  19. * @property {Function} ontransition
  20. * @property {Array.<IGraphicStateTransition|string>} transition
  21. * Transition object or a string descriptor like '* 30 0 Linear'
  22. */
  23. var transitionProperties = ['position', 'rotation', 'scale', 'style', 'shape'];
  24. /**
  25. * @module zrender/graphic/States~TransitionObject
  26. */
  27. var TransitionObject = function (opts) {
  28. if (typeof opts === 'string') {
  29. this._fromStr(opts);
  30. } else if (opts) {
  31. opts.property && (this.property = opts.property);
  32. opts.duration != null && (this.duration = opts.duration);
  33. opts.easing && (this.easing = opts.easing);
  34. opts.delay && (this.delay = opts.delay);
  35. }
  36. if (this.property !== '*') {
  37. this.property = this.property.split(',');
  38. } else {
  39. this.property = transitionProperties;
  40. }
  41. };
  42. TransitionObject.prototype = {
  43. constructor: TransitionObject,
  44. /**
  45. * List of all transition properties. Splitted by comma. Must not have spaces in the string.
  46. * e.g. 'position,style.color'. '*' will match all the valid properties.
  47. * @type {string}
  48. * @default *
  49. */
  50. property: '*',
  51. /**
  52. * @type {string}
  53. * @default 'Linear'
  54. */
  55. easing: 'Linear',
  56. /**
  57. * @type {number}
  58. * @default 'number'
  59. */
  60. duration: 500,
  61. /**
  62. * @type {number}
  63. */
  64. delay: 0,
  65. _fromStr: function (str) {
  66. var arr = str.split(/\s+/g);
  67. this.property = arr[0];
  68. this.duration = +arr[1];
  69. this.delay = +arr[2];
  70. this.easing = arr[3];
  71. }
  72. };
  73. /**
  74. * @alias module:zrender/graphic/States
  75. */
  76. var GraphicStates = function (opts) {
  77. opts = opts || {};
  78. this._states = {};
  79. /**
  80. * Target element
  81. * @type {zrender/graphic/Displayable|zrender/container/Group}
  82. */
  83. this._el = opts.el;
  84. this._subStates = [];
  85. this._transitionAnimators = [];
  86. if (opts.initialState) {
  87. this._initialState = opts.initialState;
  88. }
  89. var optsStates = opts.states;
  90. if (optsStates) {
  91. for (var name in optsStates) {
  92. if (optsStates.hasOwnProperty(name)) {
  93. var state = optsStates[name];
  94. this._addState(name, state);
  95. }
  96. }
  97. }
  98. this.setState(this._initialState);
  99. };
  100. GraphicStates.prototype = {
  101. constructor: GraphicStates,
  102. /**
  103. * All other state will be extended from initial state
  104. * @type {string}
  105. * @private
  106. */
  107. _initialState: 'normal',
  108. /**
  109. * Current state
  110. * @type {string}
  111. * @private
  112. */
  113. _currentState: '',
  114. el: function () {
  115. return this._el;
  116. },
  117. _addState: function (name, state) {
  118. this._states[name] = state;
  119. if (state.transition) {
  120. state.transition = new TransitionObject(state.transition);
  121. } // Extend from initial state
  122. if (name !== this._initialState) {
  123. this._extendFromInitial(state);
  124. } else {
  125. var el = this._el; // setState 的时候自带的 style 和 shape 都会被直接覆盖
  126. // 所以这边先把自带的 style 和 shape 扩展到初始状态中
  127. zrUtil.merge(state.style, el.style, false, false);
  128. if (state.shape) {
  129. zrUtil.merge(state.shape, el.shape, false, true);
  130. } else {
  131. state.shape = zrUtil.clone(el.shape, true);
  132. }
  133. for (var name in this._states) {
  134. if (this._states.hasOwnProperty(name)) {
  135. this._extendFromInitial(this._states[name]);
  136. }
  137. }
  138. }
  139. },
  140. _extendFromInitial: function (state) {
  141. var initialState = this._states[this._initialState];
  142. if (initialState && state !== initialState) {
  143. zrUtil.merge(state, initialState, false, true);
  144. }
  145. },
  146. setState: function (name, silent) {
  147. if (name === this._currentState && !this.transiting()) {
  148. return;
  149. }
  150. var state = this._states[name];
  151. if (state) {
  152. this._stopTransition();
  153. if (!silent) {
  154. var prevState = this._states[this._currentState];
  155. if (prevState) {
  156. prevState.onleave && prevState.onleave.call(this);
  157. }
  158. state.onenter && state.onenter.call(this);
  159. }
  160. this._currentState = name;
  161. if (this._el) {
  162. var el = this._el; // Setting attributes
  163. if (state.zlevel != null) {
  164. el.zlevel = state.zlevel;
  165. }
  166. if (state.z != null) {
  167. el.z = state.z;
  168. } // SRT
  169. state.position && vec2Copy(el.position, state.position);
  170. state.scale && vec2Copy(el.scale, state.scale);
  171. if (state.rotation != null) {
  172. el.rotation = state.rotation;
  173. } // Style
  174. if (state.style) {
  175. var initialState = this._states[this._initialState];
  176. el.style = new Style();
  177. if (initialState) {
  178. el.style.extendFrom(initialState.style, false);
  179. }
  180. if ( // Not initial state
  181. name !== this._initialState // Not copied from initial state in _extendFromInitial method
  182. && initialState.style !== state.style) {
  183. el.style.extendFrom(state.style, true);
  184. }
  185. }
  186. if (state.shape) {
  187. el.shape = zrUtil.clone(state.shape, true);
  188. }
  189. el.dirty();
  190. }
  191. }
  192. for (var i = 0; i < this._subStates.length; i++) {
  193. this._subStates.setState(name);
  194. }
  195. },
  196. getState: function () {
  197. return this._currentState;
  198. },
  199. transitionState: function (target, done) {
  200. if (target === this._currentState && !this.transiting()) {
  201. return;
  202. }
  203. var state = this._states[target];
  204. var styleShapeReg = /$[style|shape]\./;
  205. var self = this; // Animation 去重
  206. var propPathMap = {};
  207. if (state) {
  208. self._stopTransition();
  209. var el = self._el;
  210. if (state.transition && el && el.__zr) {
  211. // El can be animated
  212. var transitionCfg = state.transition;
  213. var property = transitionCfg.property;
  214. var animatingCount = 0;
  215. var animationDone = function () {
  216. animatingCount--;
  217. if (animatingCount === 0) {
  218. self.setState(target);
  219. done && done();
  220. }
  221. };
  222. for (var i = 0; i < property.length; i++) {
  223. var propName = property[i]; // Animating all the properties in style or shape
  224. if (propName === 'style' || propName === 'shape') {
  225. if (state[propName]) {
  226. for (var key in state[propName]) {
  227. /* eslint-disable max-depth */
  228. if (!state[propName].hasOwnProperty(key)) {
  229. continue;
  230. }
  231. var path = propName + '.' + key;
  232. if (propPathMap[path]) {
  233. continue;
  234. }
  235. /* eslint-enable max-depth */
  236. propPathMap[path] = 1;
  237. animatingCount += self._animProp(state, propName, key, transitionCfg, animationDone);
  238. }
  239. }
  240. } else {
  241. if (propPathMap[propName]) {
  242. continue;
  243. }
  244. propPathMap[propName] = 1; // Animating particular property in style or style
  245. if (propName.match(styleShapeReg)) {
  246. // remove 'style.', 'shape.' prefix
  247. var subProp = propName.slice(0, 5);
  248. propName = propName.slice(6);
  249. animatingCount += self._animProp(state, subProp, propName, transitionCfg, animationDone);
  250. } else {
  251. animatingCount += self._animProp(state, '', propName, transitionCfg, animationDone);
  252. }
  253. }
  254. } // No transition properties
  255. if (animatingCount === 0) {
  256. self.setState(target);
  257. done && done();
  258. }
  259. } else {
  260. self.setState(target);
  261. done && done();
  262. }
  263. }
  264. var subStates = self._subStates;
  265. for (var i = 0; i < subStates.length; i++) {
  266. subStates.transitionState(target);
  267. }
  268. },
  269. /**
  270. * Do transition animation of particular property
  271. * @param {Object} state
  272. * @param {string} subPropKey
  273. * @param {string} key
  274. * @param {Object} transitionCfg
  275. * @param {Function} done
  276. * @private
  277. */
  278. _animProp: function (state, subPropKey, key, transitionCfg, done) {
  279. var el = this._el;
  280. var stateObj = subPropKey ? state[subPropKey] : state;
  281. var elObj = subPropKey ? el[subPropKey] : el;
  282. var availableProp = stateObj && key in stateObj && elObj && key in elObj;
  283. var transitionAnimators = this._transitionAnimators;
  284. if (availableProp) {
  285. var obj = {};
  286. if (stateObj[key] === elObj[key]) {
  287. return 0;
  288. }
  289. obj[key] = stateObj[key];
  290. var animator = el.animate(subPropKey).when(transitionCfg.duration, obj).delay(transitionCfg.dealy).done(function () {
  291. var idx = zrUtil.indexOf(transitionAnimators, 1);
  292. if (idx > 0) {
  293. transitionAnimators.splice(idx, 1);
  294. }
  295. done();
  296. }).start(transitionCfg.easing);
  297. transitionAnimators.push(animator);
  298. return 1;
  299. }
  300. return 0;
  301. },
  302. _stopTransition: function () {
  303. var transitionAnimators = this._transitionAnimators;
  304. for (var i = 0; i < transitionAnimators.length; i++) {
  305. transitionAnimators[i].stop();
  306. }
  307. transitionAnimators.length = 0;
  308. },
  309. transiting: function () {
  310. return this._transitionAnimators.length > 0;
  311. },
  312. addSubStates: function (states) {
  313. this._subStates.push(states);
  314. },
  315. removeSubStates: function (states) {
  316. var idx = zrUtil.indexOf(this._subStates, states);
  317. if (idx >= 0) {
  318. this._subStates.splice(states, 1);
  319. }
  320. }
  321. };
  322. var _default = GraphicStates;
  323. module.exports = _default;