# Storage 类

# 概述

官方定义: 内容仓库

在 Zrender 实例化的时候, Storage 的实例分别作用于:

  • Painter 的实例化
  • Handler 的实例化
  • add 方法中 addRoot(el)
  • remove 方法中delRoot(el)
  • clear 方法中 delRoot()
  • dispose 方法中 dispose()

# 构造函数

# _root

加载到根节点的图形(Sharp)或者组(Group)

# _displayList

图形的绘制队列

# _displayListLen

图形的绘制队列的长度,很像以前的那种写法:

arr[arr.length] = "hello world";

用这种方式来替代 push

# 方法

# traverse

深度优先遍历所有子孙节点

traverse: function (cb, context) {
    for (var i = 0; i < this._roots.length; i++) {
        // Displayable
        this._roots[i].traverse(cb, context);
    }
}

# getDisplayList

返回所有图形的绘制队列

一共以下几个调用:

  • Handler.jsfindHover 方法中调用, updatefalse
  • Painter 中调用, update 均为 true
    • refresh 方法调用
    • getRenderedCanvas 方法调用
  • Paintersvg 实现中, refresh 方法调用, updatetrue
  • Paintervml 实现中, refresh 方法调用, updatetrue, includeIgnoretrue , 目前看来只有 vml 中才会 true

目前看来,除了 Handler.jsfindHover 方法外,其他的 update 全部为 true

当元素执行 hide 的时候 ignore = trueshow 的时候 ignore = false

getDisplayList: function (update, includeIgnore) {
    includeIgnore = includeIgnore || false;
    if (update) {
        this.updateDisplayList(includeIgnore);
    }
    return this._displayList;
}

# updateDisplayList

更新图形的绘制队列

每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中

最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列

updateDisplayList: function (includeIgnore) {
    this._displayListLen = 0;

    var roots = this._roots;
    var displayList = this._displayList;
    for (var i = 0, len = roots.length; i < len; i++) {
        this._updateAndAddDisplayable(roots[i], null, includeIgnore);
    }

    displayList.length = this._displayListLen;

    env.canvasSupported && timsort(displayList, shapeCompareFunc);
}

# _updateAndAddDisplayable

更新 Displayable 数据, 处理剪切相关,然后向 _displayList 中增加数据

_updateAndAddDisplayable: function (el, clipPaths, includeIgnore) {

        if (el.ignore && !includeIgnore) {
            return;
        }

        el.beforeUpdate();

        if (el.__dirty) {
            el.update();
        }

        el.afterUpdate();

        var userSetClipPath = el.clipPath;
        if (userSetClipPath) {

            // FIXME 效率影响
            if (clipPaths) {
                clipPaths = clipPaths.slice();
            }
            else {
                clipPaths = [];
            }

            var currentClipPath = userSetClipPath;
            var parentClipPath = el;
            // Recursively add clip path
            while (currentClipPath) {
                // clipPath 的变换是基于使用这个 clipPath 的元素
                currentClipPath.parent = parentClipPath;
                currentClipPath.updateTransform();

                clipPaths.push(currentClipPath);

                parentClipPath = currentClipPath;
                currentClipPath = currentClipPath.clipPath;
            }
        }

        if (el.isGroup) {
            var children = el._children;

            for (var i = 0; i < children.length; i++) {
                var child = children[i];

                // Force to mark as dirty if group is dirty
                // FIXME __dirtyPath ?
                if (el.__dirty) {
                    child.__dirty = true;
                }

                this._updateAndAddDisplayable(child, clipPaths, includeIgnore);
            }

            // Mark group clean here
            el.__dirty = false;

        }
        else {
            el.__clipPaths = clipPaths;

            this._displayList[this._displayListLen++] = el;
        }
    }

# addRoot

添加图形(Shape)或者组(Group)到根节点

addRoot: function (el) {
    if (el.__storage === this) {
        return;
    }
    if (el instanceof Group) {
        el.addChildrenToStorage(this);
    }
    this.addToStorage(el);
    this._roots.push(el);
}

# addToStorage

将元素的 __storage 指向 this, 取消元素的需要更新请求(_dirty)

在 zrender 中的 add 方法中,调用 addRoot 时,应增加了需要更新请求 this._needsRefresh = true

addToStorage: function (el) {
    if (el) {
        el.__storage = this;
        el.dirty(false);
    }
    return this;
}

# delRoot

删除指定的图形(Shape)或者组(Group)

delRoot: function (el) {
    if (el == null) {
        // 不指定el清空
        for (var i = 0; i < this._roots.length; i++) {
            var root = this._roots[i];
            if (root instanceof Group) {
                root.delChildrenFromStorage(this);
            }
        }

        this._roots = [];
        this._displayList = [];
        this._displayListLen = 0;

        return;
    }

    if (el instanceof Array) {
        for (var i = 0, l = el.length; i < l; i++) {
            this.delRoot(el[i]);
        }
        return;
    }


    var idx = util.indexOf(this._roots, el);
    if (idx >= 0) {
        this.delFromStorage(el);
        this._roots.splice(idx, 1);
        if (el instanceof Group) {
            el.delChildrenFromStorage(this);
        }
    }
}

# delFromStorage

delFromStorage: function (el) {
    if (el) {
        el.__storage = null;
    }

    return this;
}

# dispose

清空并且释放Storage

dispose: function () {
    this._renderList = this._roots = null;
}

# displayableSortFunc

function shapeCompareFunc(a, b) {
    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;
}