Storage.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import * as util from './core/util';
  2. import env from './core/env';
  3. import Group from './container/Group';
  4. // Use timsort because in most case elements are partially sorted
  5. // https://jsfiddle.net/pissang/jr4x7mdm/8/
  6. import timsort from './core/timsort';
  7. function shapeCompareFunc(a, b) {
  8. if (a.zlevel === b.zlevel) {
  9. if (a.z === b.z) {
  10. // if (a.z2 === b.z2) {
  11. // // FIXME Slow has renderidx compare
  12. // // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement
  13. // // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012
  14. // return a.__renderidx - b.__renderidx;
  15. // }
  16. return a.z2 - b.z2;
  17. }
  18. return a.z - b.z;
  19. }
  20. return a.zlevel - b.zlevel;
  21. }
  22. /**
  23. * 内容仓库 (M)
  24. * @alias module:zrender/Storage
  25. * @constructor
  26. */
  27. var Storage = function () { // jshint ignore:line
  28. this._roots = [];
  29. this._displayList = [];
  30. this._displayListLen = 0;
  31. };
  32. Storage.prototype = {
  33. constructor: Storage,
  34. /**
  35. * @param {Function} cb
  36. *
  37. */
  38. traverse: function (cb, context) {
  39. for (var i = 0; i < this._roots.length; i++) {
  40. this._roots[i].traverse(cb, context);
  41. }
  42. },
  43. /**
  44. * 返回所有图形的绘制队列
  45. * @param {boolean} [update=false] 是否在返回前更新该数组
  46. * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组, 在 update 为 true 的时候有效
  47. *
  48. * 详见{@link module:zrender/graphic/Displayable.prototype.updateDisplayList}
  49. * @return {Array.<module:zrender/graphic/Displayable>}
  50. */
  51. getDisplayList: function (update, includeIgnore) {
  52. includeIgnore = includeIgnore || false;
  53. if (update) {
  54. this.updateDisplayList(includeIgnore);
  55. }
  56. return this._displayList;
  57. },
  58. /**
  59. * 更新图形的绘制队列。
  60. * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中,
  61. * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列
  62. * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组
  63. */
  64. updateDisplayList: function (includeIgnore) {
  65. this._displayListLen = 0;
  66. var roots = this._roots;
  67. var displayList = this._displayList;
  68. for (var i = 0, len = roots.length; i < len; i++) {
  69. this._updateAndAddDisplayable(roots[i], null, includeIgnore);
  70. }
  71. displayList.length = this._displayListLen;
  72. env.canvasSupported && timsort(displayList, shapeCompareFunc);
  73. },
  74. _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) {
  75. if (el.ignore && !includeIgnore) {
  76. return;
  77. }
  78. el.beforeUpdate();
  79. if (el.__dirty) {
  80. el.update();
  81. }
  82. el.afterUpdate();
  83. var userSetClipPath = el.clipPath;
  84. if (userSetClipPath) {
  85. // FIXME 效率影响
  86. if (clipPaths) {
  87. clipPaths = clipPaths.slice();
  88. }
  89. else {
  90. clipPaths = [];
  91. }
  92. var currentClipPath = userSetClipPath;
  93. var parentClipPath = el;
  94. // Recursively add clip path
  95. while (currentClipPath) {
  96. // clipPath 的变换是基于使用这个 clipPath 的元素
  97. currentClipPath.parent = parentClipPath;
  98. currentClipPath.updateTransform();
  99. clipPaths.push(currentClipPath);
  100. parentClipPath = currentClipPath;
  101. currentClipPath = currentClipPath.clipPath;
  102. }
  103. }
  104. if (el.isGroup) {
  105. var children = el._children;
  106. for (var i = 0; i < children.length; i++) {
  107. var child = children[i];
  108. // Force to mark as dirty if group is dirty
  109. // FIXME __dirtyPath ?
  110. if (el.__dirty) {
  111. child.__dirty = true;
  112. }
  113. this._updateAndAddDisplayable(child, clipPaths, includeIgnore);
  114. }
  115. // Mark group clean here
  116. el.__dirty = false;
  117. }
  118. else {
  119. el.__clipPaths = clipPaths;
  120. this._displayList[this._displayListLen++] = el;
  121. }
  122. },
  123. /**
  124. * 添加图形(Shape)或者组(Group)到根节点
  125. * @param {module:zrender/Element} el
  126. */
  127. addRoot: function (el) {
  128. if (el.__storage === this) {
  129. return;
  130. }
  131. if (el instanceof Group) {
  132. el.addChildrenToStorage(this);
  133. }
  134. this.addToStorage(el);
  135. this._roots.push(el);
  136. },
  137. /**
  138. * 删除指定的图形(Shape)或者组(Group)
  139. * @param {string|Array.<string>} [el] 如果为空清空整个Storage
  140. */
  141. delRoot: function (el) {
  142. if (el == null) {
  143. // 不指定el清空
  144. for (var i = 0; i < this._roots.length; i++) {
  145. var root = this._roots[i];
  146. if (root instanceof Group) {
  147. root.delChildrenFromStorage(this);
  148. }
  149. }
  150. this._roots = [];
  151. this._displayList = [];
  152. this._displayListLen = 0;
  153. return;
  154. }
  155. if (el instanceof Array) {
  156. for (var i = 0, l = el.length; i < l; i++) {
  157. this.delRoot(el[i]);
  158. }
  159. return;
  160. }
  161. var idx = util.indexOf(this._roots, el);
  162. if (idx >= 0) {
  163. this.delFromStorage(el);
  164. this._roots.splice(idx, 1);
  165. if (el instanceof Group) {
  166. el.delChildrenFromStorage(this);
  167. }
  168. }
  169. },
  170. addToStorage: function (el) {
  171. if (el) {
  172. el.__storage = this;
  173. el.dirty(false);
  174. }
  175. return this;
  176. },
  177. delFromStorage: function (el) {
  178. if (el) {
  179. el.__storage = null;
  180. }
  181. return this;
  182. },
  183. /**
  184. * 清空并且释放Storage
  185. */
  186. dispose: function () {
  187. this._renderList =
  188. this._roots = null;
  189. },
  190. displayableSortFunc: shapeCompareFunc
  191. };
  192. export default Storage;