123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- import * as util from './core/util';
- import Group, { GroupLike } from './graphic/Group';
- import Element from './Element';
- // Use timsort because in most case elements are partially sorted
- // https://jsfiddle.net/pissang/jr4x7mdm/8/
- import timsort from './core/timsort';
- import Displayable from './graphic/Displayable';
- import Path from './graphic/Path';
- import { REDRAW_BIT } from './graphic/constants';
- let invalidZErrorLogged = false;
- function logInvalidZError() {
- if (invalidZErrorLogged) {
- return;
- }
- invalidZErrorLogged = true;
- console.warn('z / z2 / zlevel of displayable is invalid, which may cause unexpected errors');
- }
- function shapeCompareFunc(a: Displayable, b: Displayable) {
- if (a.zlevel === b.zlevel) {
- if (a.z === b.z) {
- return a.z2 - b.z2;
- }
- return a.z - b.z;
- }
- return a.zlevel - b.zlevel;
- }
- export default class Storage {
- private _roots: Element[] = []
- private _displayList: Displayable[] = []
- private _displayListLen = 0
- traverse<T>(
- cb: (this: T, el: Element) => void,
- context?: T
- ) {
- for (let i = 0; i < this._roots.length; i++) {
- this._roots[i].traverse(cb, context);
- }
- }
- /**
- * get a list of elements to be rendered
- *
- * @param {boolean} update whether to update elements before return
- * @param {DisplayParams} params options
- * @return {Displayable[]} a list of elements
- */
- getDisplayList(update?: boolean, includeIgnore?: boolean): Displayable[] {
- includeIgnore = includeIgnore || false;
- const displayList = this._displayList;
- // If displaylist is not created yet. Update force
- if (update || !displayList.length) {
- this.updateDisplayList(includeIgnore);
- }
- return displayList;
- }
- /**
- * 更新图形的绘制队列。
- * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中,
- * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列
- */
- updateDisplayList(includeIgnore?: boolean) {
- this._displayListLen = 0;
- const roots = this._roots;
- const displayList = this._displayList;
- for (let i = 0, len = roots.length; i < len; i++) {
- this._updateAndAddDisplayable(roots[i], null, includeIgnore);
- }
- displayList.length = this._displayListLen;
- timsort(displayList, shapeCompareFunc);
- }
- private _updateAndAddDisplayable(
- el: Element,
- clipPaths: Path[],
- includeIgnore?: boolean
- ) {
- if (el.ignore && !includeIgnore) {
- return;
- }
- el.beforeUpdate();
- el.update();
- el.afterUpdate();
- const userSetClipPath = el.getClipPath();
- if (el.ignoreClip) {
- clipPaths = null;
- }
- else if (userSetClipPath) {
- // FIXME 效率影响
- if (clipPaths) {
- clipPaths = clipPaths.slice();
- }
- else {
- clipPaths = [];
- }
- let currentClipPath = userSetClipPath;
- let parentClipPath = el;
- // Recursively add clip path
- while (currentClipPath) {
- // clipPath 的变换是基于使用这个 clipPath 的元素
- // TODO: parent should be group type.
- currentClipPath.parent = parentClipPath as Group;
- currentClipPath.updateTransform();
- clipPaths.push(currentClipPath);
- parentClipPath = currentClipPath;
- currentClipPath = currentClipPath.getClipPath();
- }
- }
- // ZRText and Group and combining morphing Path may use children
- if ((el as GroupLike).childrenRef) {
- const children = (el as GroupLike).childrenRef();
- for (let i = 0; i < children.length; i++) {
- const child = children[i];
- // Force to mark as dirty if group is dirty
- if (el.__dirty) {
- child.__dirty |= REDRAW_BIT;
- }
- this._updateAndAddDisplayable(child, clipPaths, includeIgnore);
- }
- // Mark group clean here
- el.__dirty = 0;
- }
- else {
- const disp = el as Displayable;
- // Element is displayable
- if (clipPaths && clipPaths.length) {
- disp.__clipPaths = clipPaths;
- }
- else if (disp.__clipPaths && disp.__clipPaths.length > 0) {
- disp.__clipPaths = [];
- }
- // Avoid invalid z, z2, zlevel cause sorting error.
- if (isNaN(disp.z)) {
- logInvalidZError();
- disp.z = 0;
- }
- if (isNaN(disp.z2)) {
- logInvalidZError();
- disp.z2 = 0;
- }
- if (isNaN(disp.zlevel)) {
- logInvalidZError();
- disp.zlevel = 0;
- }
- this._displayList[this._displayListLen++] = disp;
- }
- // Add decal
- const decalEl = (el as Path).getDecalElement && (el as Path).getDecalElement();
- if (decalEl) {
- this._updateAndAddDisplayable(decalEl, clipPaths, includeIgnore);
- }
- // Add attached text element and guide line.
- const textGuide = el.getTextGuideLine();
- if (textGuide) {
- this._updateAndAddDisplayable(textGuide, clipPaths, includeIgnore);
- }
- const textEl = el.getTextContent();
- if (textEl) {
- this._updateAndAddDisplayable(textEl, clipPaths, includeIgnore);
- }
- }
- /**
- * 添加图形(Displayable)或者组(Group)到根节点
- */
- addRoot(el: Element) {
- if (el.__zr && el.__zr.storage === this) {
- return;
- }
- this._roots.push(el);
- }
- /**
- * 删除指定的图形(Displayable)或者组(Group)
- * @param el
- */
- delRoot(el: Element | Element[]) {
- if (el instanceof Array) {
- for (let i = 0, l = el.length; i < l; i++) {
- this.delRoot(el[i]);
- }
- return;
- }
- const idx = util.indexOf(this._roots, el);
- if (idx >= 0) {
- this._roots.splice(idx, 1);
- }
- }
- delAllRoots() {
- this._roots = [];
- this._displayList = [];
- this._displayListLen = 0;
- return;
- }
- getRoots() {
- return this._roots;
- }
- /**
- * 清空并且释放Storage
- */
- dispose() {
- this._displayList = null;
- this._roots = null;
- }
- displayableSortFunc = shapeCompareFunc
- }
|