2

I'm playing with Kinetic and can't seem to figure out why a group I'm cloning won't appear.

Fiddle: http://jsfiddle.net/DgwLd/3/

I can clone shapes no problem - it's just groups that don't appear. The documentation confirms that groups can be cloned, so I'm not sure what's up here. Here's the code from the Fiddle:

//group and original circle - appears fine
layer.add(new Kinetic.Group({id: 'group'}));
stage.get('#group')[0].add(new Kinetic.Circle({
    fill: 'orange',
    x: 200,
    y: 50,
    radius: 30
}));

//clone of group at different Y pos - doesn't appear
layer.add(stage.get('#group')[0].clone({y: 120}));

UPDATE - it seems cloning a group doesn't make a deep copy. This is verifiable by console logging the group - console.log(stage.get('#group2')) and you'll see its children collection is empty). Is this a bug? Not sure why you'd ever want to clone a group without its constituents.

Any thoughts? Thanks in advance.

Mitya
  • 33,629
  • 9
  • 60
  • 107

1 Answers1

1

Seems like you figured it out yourself - indeed, making a group clone, or in fact a clone of any container, performs a shallow copy.

Actually, looking at the source code may be more informative than anything else:

clone: function(obj) {
    // instantiate new node
    var classType = this.shapeType || this.nodeType;
    var node = new Kinetic[classType](this.attrs);

    /*
     * copy over user listeners
     */
    for(var key in this.eventListeners) {
        var allListeners = this.eventListeners[key];
        for(var n = 0; n < allListeners.length; n++) {
            var listener = allListeners[n];
            /*
             * don't include kinetic namespaced listeners because
             *  these are generated by the constructors
             */
            if(listener.name.indexOf('kinetic') < 0) {
                // if listeners array doesn't exist, then create it
                if(!node.eventListeners[key]) {
                    node.eventListeners[key] = [];
                }
                node.eventListeners[key].push(listener);
            }
        }
    }

    // apply attr overrides
    node.setAttrs(obj);
    return node;
}

As you can see, all it does is instantiate a new node with the current node's attributes, and then copy over the event listeners. This is probably by design - generally you want to take the path of least resistance, and this implementation is alright when you consider that the .clone() method exists for any general node (it's not specifically for containers).

It does seem like there should be something like a .deepclone() method for containers specifically, though. Maybe something like:

deepclone: function(obj) {
    var node = this.clone(obj);

    if (this.children) {
        for (var i = 0; i < this.children.length; i++) {
            node.add(this.children[i].clone();
        }
    }

    return node;
}
voithos
  • 68,482
  • 12
  • 101
  • 116
  • Thanks for your help. I get the reasoning for shallow cloning but, as you say, I certainly think there should be a means to deep clone. In my case, I have complex groups representing characters in a game, and I want to clone the whole group. I could put each character on a layer, I guess. Which further blurs the lines of definition between groups and layers, but I guess that's OK. – Mitya Aug 28 '12 at 16:03