Storage.ts 6.7 KB

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