Storage.ts 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import * as util from './core/util';
  2. import Group, { GroupLike } from './graphic/Group';
  3. import Element from './Element';
  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. import Displayable from './graphic/Displayable';
  8. import Path from './graphic/Path';
  9. import { REDRAW_BIT } from './graphic/constants';
  10. import { NullUndefined } from './core/types';
  11. let invalidZErrorLogged = false;
  12. function logInvalidZError() {
  13. if (invalidZErrorLogged) {
  14. return;
  15. }
  16. invalidZErrorLogged = true;
  17. console.warn('z / z2 / zlevel of displayable is invalid, which may cause unexpected errors');
  18. }
  19. function shapeCompareFunc(a: Displayable, b: Displayable) {
  20. if (a.zlevel === b.zlevel) {
  21. if (a.z === b.z) {
  22. return a.z2 - b.z2;
  23. }
  24. return a.z - b.z;
  25. }
  26. return a.zlevel - b.zlevel;
  27. }
  28. export default class Storage {
  29. private _roots: Element[] = []
  30. private _displayList: Displayable[] = []
  31. private _displayListLen = 0
  32. traverse<T>(
  33. cb: (this: T, el: Element) => void,
  34. context?: T
  35. ) {
  36. for (let i = 0; i < this._roots.length; i++) {
  37. this._roots[i].traverse(cb, context);
  38. }
  39. }
  40. /**
  41. * get a list of elements to be rendered
  42. *
  43. * @param {boolean} update whether to update elements before return
  44. * @param {DisplayParams} params options
  45. * @return {Displayable[]} a list of elements
  46. */
  47. getDisplayList(update?: boolean, includeIgnore?: boolean): Displayable[] {
  48. includeIgnore = includeIgnore || false;
  49. const displayList = this._displayList;
  50. // If displaylist is not created yet. Update force
  51. if (update || !displayList.length) {
  52. this.updateDisplayList(includeIgnore);
  53. }
  54. return displayList;
  55. }
  56. /**
  57. * 更新图形的绘制队列。
  58. * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中,
  59. * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列
  60. */
  61. updateDisplayList(includeIgnore?: boolean) {
  62. this._displayListLen = 0;
  63. const roots = this._roots;
  64. const displayList = this._displayList;
  65. for (let i = 0, len = roots.length; i < len; i++) {
  66. this._updateAndAddDisplayable(roots[i], null, includeIgnore);
  67. }
  68. displayList.length = this._displayListLen;
  69. // PENDING: Indicatively, it may cost over 10~20ms when list length is over 1e5.
  70. // See PENDING_SEPARATE_DISPLAY_LIST
  71. timsort(displayList, shapeCompareFunc);
  72. }
  73. private _updateAndAddDisplayable(
  74. el: Element,
  75. parentClipPaths: Path[] | NullUndefined,
  76. includeIgnore?: boolean
  77. ) {
  78. if (el.ignore && !includeIgnore) {
  79. return;
  80. }
  81. el.beforeUpdate();
  82. el.update();
  83. el.afterUpdate();
  84. const userSetClipPath = el.getClipPath();
  85. const parentHasClipPaths = parentClipPaths && parentClipPaths.length;
  86. let clipPathIdx = 0;
  87. let thisClipPaths = el.__clipPaths;
  88. if (!el.ignoreClip
  89. && (parentHasClipPaths || userSetClipPath)
  90. ) { // has clipPath in this pass
  91. if (!thisClipPaths) {
  92. thisClipPaths = el.__clipPaths = [];
  93. }
  94. if (parentHasClipPaths) {
  95. // PENDING: performance?
  96. for (let idx = 0; idx < parentClipPaths.length; idx++) {
  97. thisClipPaths[clipPathIdx++] = parentClipPaths[idx];
  98. }
  99. }
  100. let currentClipPath = userSetClipPath;
  101. let parentClipPath = el;
  102. // Recursively add clip path
  103. while (currentClipPath) {
  104. // clipPath 的变换是基于使用这个 clipPath 的元素
  105. // TODO: parent should be group type.
  106. currentClipPath.parent = parentClipPath as Group;
  107. currentClipPath.updateTransform();
  108. thisClipPaths[clipPathIdx++] = currentClipPath;
  109. parentClipPath = currentClipPath;
  110. currentClipPath = currentClipPath.getClipPath();
  111. }
  112. }
  113. if (thisClipPaths) { // Remove other old clipPath in array.
  114. thisClipPaths.length = clipPathIdx;
  115. }
  116. // ZRText and Group and combining morphing Path may use children
  117. if ((el as GroupLike).childrenRef) {
  118. const children = (el as GroupLike).childrenRef();
  119. for (let i = 0; i < children.length; i++) {
  120. const child = children[i];
  121. // Force to mark as dirty if group is dirty
  122. if (el.__dirty) {
  123. child.__dirty |= REDRAW_BIT;
  124. }
  125. this._updateAndAddDisplayable(child, thisClipPaths, includeIgnore);
  126. }
  127. // Mark group clean here
  128. el.__dirty = 0;
  129. }
  130. else {
  131. const disp = el as Displayable;
  132. // Avoid invalid z, z2, zlevel cause sorting error.
  133. if (isNaN(disp.z)) {
  134. logInvalidZError();
  135. disp.z = 0;
  136. }
  137. if (isNaN(disp.z2)) {
  138. logInvalidZError();
  139. disp.z2 = 0;
  140. }
  141. if (isNaN(disp.zlevel)) {
  142. logInvalidZError();
  143. disp.zlevel = 0;
  144. }
  145. this._displayList[this._displayListLen++] = disp;
  146. }
  147. // Add decal
  148. const decalEl = (el as Path).getDecalElement && (el as Path).getDecalElement();
  149. if (decalEl) {
  150. this._updateAndAddDisplayable(decalEl, thisClipPaths, includeIgnore);
  151. }
  152. // Add attached text element and guide line.
  153. const textGuide = el.getTextGuideLine();
  154. if (textGuide) {
  155. this._updateAndAddDisplayable(textGuide, thisClipPaths, includeIgnore);
  156. }
  157. const textEl = el.getTextContent();
  158. if (textEl) {
  159. this._updateAndAddDisplayable(textEl, thisClipPaths, includeIgnore);
  160. }
  161. }
  162. /**
  163. * 添加图形(Displayable)或者组(Group)到根节点
  164. */
  165. addRoot(el: Element) {
  166. if (el.__zr && el.__zr.storage === this) {
  167. return;
  168. }
  169. this._roots.push(el);
  170. }
  171. /**
  172. * 删除指定的图形(Displayable)或者组(Group)
  173. * @param el
  174. */
  175. delRoot(el: Element | Element[]) {
  176. if (el instanceof Array) {
  177. for (let i = 0, l = el.length; i < l; i++) {
  178. this.delRoot(el[i]);
  179. }
  180. return;
  181. }
  182. const idx = util.indexOf(this._roots, el);
  183. if (idx >= 0) {
  184. this._roots.splice(idx, 1);
  185. }
  186. }
  187. delAllRoots() {
  188. this._roots = [];
  189. this._displayList = [];
  190. this._displayListLen = 0;
  191. return;
  192. }
  193. getRoots() {
  194. return this._roots;
  195. }
  196. /**
  197. * 清空并且释放Storage
  198. */
  199. dispose() {
  200. this._displayList = null;
  201. this._roots = null;
  202. }
  203. displayableSortFunc = shapeCompareFunc
  204. }