Snap之Set - bobby169/Snap.svgDoc GitHub Wiki

[TOC]

Set

Set本质是一个数组,可以对添加到数组中的svg批量执行pushpopforEachanimateremovebindattrclearspliceexcludeinsertAftergetBBoxclone等批量操作。

var Set = function (items) {
    this.items = [];
    this.bindings = {};
    this.length = 0;
    this.type = "set";
    if (items) {
        for (var i = 0, ii = items.length; i < ii; i++) {
            if (items[i]) {
                // 以下标的形式,复制数组数据作为Set类的属性,且让Set具有类又具有数组的特性。因为有数组下标又有length
                this[this.items.length] = this.items[this.items.length] = items[i];
                this.length++;
            }
        }
    }
},
setproto = Set.prototype;

Set与Paper.g或Paper.group的区别

Set更像createjs中的Container,本质是一个数组,是虚拟的。但可以对整个数组批量操作。而Paper.group是Svg中的<g>标签,是实体的标签。因为createjs中只有canvas一个标签,所以Container是一个数组。而Svg本身有<g>标签,添加Set的支持,在不用<g>标签的情况下可以灵活批量处理svg中的元素。

Set本身是一个类,以下标的形式,复制数组数据作为Set类的属性,且让Set具有类又具有数组的特性。因为有数组下标又有length。但Set终旧是一个类,且是一个伪数组,所以后面的操作就是模拟数组的pushpopforEachsplice等方法。

es6 Set本身是一个构造函数,集成了数组和类的于一身

let svg = Snap("#svg");
let c1 = svg.paper.circle(50,50,40);
let c2 = svg.paper.circle(150,50,40);

let set = Snap.set(c1, c2) // 注意set为小写
set.exclude(c1)

set.attr({
    fill: "#f00",
    stroke: "#0f0",
    strokeWidth: 5
})

下面我们用es6原生的Set构造函数

Set.prototype.attr = function (value) {
    this.forEach((v) => {
        v.attr(value)
    })
}

let svg = Snap("#svg");
let c1 = svg.paper.circle(50,50,40);
let c2 = svg.paper.circle(150,50,40);
let set = new Set([c1,c2])
set.delete(c1)

set.attr({
    fill: "#f00",
    stroke: "#0f0",
    strokeWidth: 5
});

Set.push() 相当于es6 Set.prototype.add()方法

setproto.push = function () {
        var item,
        len;
    for (var i = 0, ii = arguments.length; i < ii; i++) {
        item = arguments[i];
        if (item) {
            len = this.items.length;
            this[len] = this.items[len] = item;
            this.length++;
        }
    }
    return this;
};

Set.exclude(element) 相当于es6 Set.prototype.delete()方法

setproto.exclude = function (el) {
    for (var i = 0, ii = this.length; i < ii; i++) {
        if (this[i] == el) {
            this.splice(i, 1);
            return true;
        }
    }
    return false;
};

Set.pop()

Removes last element and returns it

setproto.pop = function () {
    this.length && delete this[this.length--];
    return this.items.pop();
};

Set.clear() 相当于es6 Set.prototype.clear()方法

Removes all elements from the set

setproto.clear = function () {
    while (this.length) {
        this.pop();
    }
};

Set.remove()

Removes all children of the set.

setproto.remove = function () {
    while (this.length) {
        this.pop().remove();
    }
    return this;
};

Set.forEach() 相当于es6 Set.prototype.forEach()方法

setproto.forEach = function (callback, thisArg) {
    for (var i = 0, ii = this.items.length; i < ii; i++) {
        if (callback.call(thisArg, this.items[i], i) === false) {
            return this;
        }
    }
    return this;
};

Set.insertAfter(el)

Inserts set elements after given element.

setproto.insertAfter = function (el) {
    var i = this.items.length;
    while (i--) {
        this.items[i].insertAfter(el);
    }
    return this;
};

Set.attr(value)

setproto.attr = function (value) {
    var unbound = {};
    for (var k in value) {
        if (this.bindings[k]) {
            this.bindings[k](value[k]);
        } else {
            unbound[k] = value[k];
        }
    }
    for (var i = 0, ii = this.items.length; i < ii; i++) {
        this.items[i].attr(unbound);
    }
    return this;
};

es6 Set实现

Set.prototype.attr = function (value) {
    this.forEach((v) => {
        v.attr(value)
    })
}

Set.animate(attrs, ms, easing, callback)

setproto.animate = function (attrs, ms, easing, callback) {
    if (typeof easing == "function" && !easing.length) {
        callback = easing;
        easing = mina.linear;
    }
    if (attrs instanceof Snap._.Animation) {
        callback = attrs.callback;
        easing = attrs.easing;
        ms = easing.dur;
        attrs = attrs.attr;
    }
    var args = arguments;
    if (Snap.is(attrs, "array") && Snap.is(args[args.length - 1], "array")) {
        var each = true;
    }
    var begin,
        handler = function () {
            if (begin) {
                this.b = begin;
            } else {
                begin = this.b;
            }
        },
        cb = 0,
        set = this,
        callbacker = callback && function () {
            if (++cb == set.length) {
                callback.call(this);
            }
        };
    return this.forEach(function (el, i) {
        eve.once("snap.animcreated." + el.id, handler);
        if (each) {
            args[i] && el.animate.apply(el, args[i]);
        } else {
            el.animate(attrs, ms, easing, callbacker);
        }
    });
};

用es6实现

Set.prototype.animate = setproto.animate

示例:

let s = Snap('#svg')

let circle = s.circle(10, 20, 30);
let square = s.rect(60, 60, 30, 30);
// let set = new Set([circle, square]);
let set = Snap.set(circle, square);
let result = set.animate({opacity: .5}, 1000, function() {
    result.forEach(function (el) {
        var o = el.attr("opacity");
        console.info(o)
    });
});

Set.getBBox()

返回集合中元素的边界框bounding box描述

setproto.getBBox = function () {
    var x = [],
        y = [],
        x2 = [],
        y2 = [];
    for (var i = this.items.length; i--;) if (!this.items[i].removed) {
        var box = this.items[i].getBBox();
        x.push(box.x);
        y.push(box.y);
        x2.push(box.x + box.width);
        y2.push(box.y + box.height);
    }
    x = Math.min.apply(0, x);
    y = Math.min.apply(0, y);
    x2 = Math.max.apply(0, x2);
    y2 = Math.max.apply(0, y2);
    return {
        x: x,
        y: y,
        x2: x2,
        y2: y2,
        width: x2 - x,
        height: y2 - y,
        cx: x + (x2 - x) / 2,
        cy: y + (y2 - y) / 2
    };
};

es6 Set实现

Set.prototype.getBBox = function () {
    let x = [],
        y = [],
        x2 = [],
        y2 = [];

    this.forEach((v) => {
        if(!v.removed) {
            let box = v.getBBox();
            x.push(box.x);
            y.push(box.y);
            x2.push(box.x + box.width);
            y2.push(box.y + box.height);
        }
    })

    x = Math.min.apply(0, x);
    y = Math.min.apply(0, y);
    x2 = Math.max.apply(0, x2);
    y2 = Math.max.apply(0, y2);
    return {
        x: x,
        y: y,
        x2: x2,
        y2: y2,
        width: x2 - x,
        height: y2 - y,
        cx: x + (x2 - x) / 2,
        cy: y + (y2 - y) / 2
    };
}

示例:

let svg = Snap("#svg");
let c1 = svg.paper.circle(50,50,40);
let c2 = svg.paper.circle(150,50,40);

let set = Snap.set(c1, c2) // 注意set为小写

// let set = new Set([c1, c2])

set.attr({
    fill: "#f00",
    stroke: "#0f0",
    strokeWidth: 5
})

let bbox = set.getBBox()
console.info(bbox)

let rect = svg.paper.rect(bbox.x, bbox.y, bbox.width, bbox.height).attr({
    opacity: 0.2,
    fill: '#00f'
})