clone() but keep child IDs?

630 views
Skip to first unread message

Ryo

unread,
Mar 19, 2014, 3:32:20 PM3/19/14
to sna...@googlegroups.com
Hello.

So I'm loading an external svg (which has multiple groups/layers so it's like a spritesheet) and keeping the originals in an Object. I don't append instances until I need them.

The thing is that when I do instantiate from my "appendix" I have to clone the object using .clone() but it changes all the IDs of my paths/rects to generated ones so I can't .select() recolor them. And all my "sprites" are going to have different amounts and orderings of paths/rects so it wouldn't make sense to try to hard code the layers and ids for each sprite. Is there some kind of for loop I can utilize to reset the IDs of just the layers of my cloned objects?

Some example code:

var appendix = {};

function loadItems() {
    Snap.load("res/items.svg", function(f) {
        var potion = f.select("#potion");
        addToAppendix("potion", potion); //somehow passing the selected chunk as a parameter makes it possible to add to the master list
    });
}

function addToAppendix(name, o) {
    appendix[name] = o;
}

function createItem() {
    var pot = appendix["potion"].clone();
    map.add(pot); //map works fine
    pot.select("#hereIsWhereIWantTheDefaultIDs").attr({fill: "#f00"}); //change liquid color
}

Ian

unread,
Mar 20, 2014, 5:46:25 AM3/20/14
to sna...@googlegroups.com
Just thinking, could you give them a specific class name, and select by that, as that won't change ? Otherwise you could maybe utilise a selectAll and forEach loop or something maybe ?

Thomas Shinnick

unread,
Mar 20, 2014, 2:33:00 PM3/20/14
to sna...@googlegroups.com
Of course the 'rule' is that a particular id name must be unique in the DOM tree.  That is so that you can getElementById() (or use Snap's .select()/.selectAll() from the document root and find only that one particular DOM node.  So having more than one node with the same 'name' is very very bad.  :-)    (Umm, I do it sometimes, but I have very very good reasons... yeah, that's the story!)

But if you are keeping the Snap.load()'ed document fragments separate and out of the DOM tree, and you would only be adding your chosen pieces of the fragments just once, then you wouldn't be violating the 'rule', right?

Snap.svg is mashing the original id's when cloning because it is worried about violating the 'rule' about id name uniqueness, and also because it needs to do bookkeeping about <use>/.etc. and links between SVG elements using xlink:href and url().

The actual clone process is (looks like) just a few lines.  Maybe coded up as a plugin?  I'll try to do that, but that would be 3-4 hours from now.

Thomas Shinnick

unread,
Mar 20, 2014, 5:48:04 PM3/20/14
to sna...@googlegroups.com
After you have pulled in the Snap.svg.js module, and before your code, add this in a <script>

  <script>
   
Snap.plugin( function( Snap, Element, Paper, global ) {
     
Element.prototype.cloneKeepIds = function() {
       
var cloneNode = this.node.cloneNode(true);
       
var cloneElt  = new Element(cloneNode);
        cloneElt
.insertAfter(this);
       
return cloneElt;
     
};
   
});
 
</script>

and use it (when needed, with caution, buy insurance first, etc.) instead of Element.clone()

      Snap.load(theResource, function(frag) {
   
//    console.log('    Snap.load() returned "',frag,'"');
         
var myGroup = frag.select('#myShapes');
   
//    console.log('      original group:  ', myGroup);
   
//    var myClonedGroup = myGroup.clone();
   
//    console.log('      cloned group:    ', myClonedGroup);
         
var myClonedOther = myGroup.cloneKeepIds();
          console
.log('      alternate clone: ', myClonedOther);
       
:      :      :      :

Again, Snap.svg's Element.clone() is trying very hard not to mess up the links/relationships between elements if such are used, which is why it takes control of  .id  values.  But the above skips over all that and chooses to implement the simple and dumb clone.

I wonder if we could get a Boolean added to Element.clone() ?  :-)

Ryo

unread,
Mar 21, 2014, 1:55:27 AM3/21/14
to sna...@googlegroups.com
Appreciate the replys guys. I'm gonna try your code Thomas, but I wonder if Ian's approach will make more sense. Each instance should have its own unique IDs, and since SVGs are simply XML I can add custom attributes (or a class) to identify layers (thank you Inkscape). I'm guessing I have to rethink how my content (items/monsters) is layed out, and have something like standard recolor layer names. That'd make it much simpler.

Carl Hunt

unread,
Mar 28, 2014, 11:11:28 AM3/28/14
to sna...@googlegroups.com
I'm loading an external svg to act as a spritesheet for an app as well and I'm curious how you deal with placement of groups in the spritesheet svg vs. in the app. In my case I have a set of icons from a designer that are laid out in a grid. I'm cloning the individual icons to place them in my app. Right now I calculate the offset that each icon is from the origin and store that along with the sprite data. I use that offset in calculating the transforms.

Ryo

unread,
Apr 3, 2014, 1:41:36 PM4/3/14
to sna...@googlegroups.com
I apologize for the delay. Personally I put all of them right on top of each other in different layers. As long as you put each one in its own group you can extract each sprite. That way you won't have to bother with those offsets. And when designing you just need to hide other layers. And yeah, classes seem to be the way to go for multiple sprites that can be recolored.

Carl Hunt

unread,
Apr 4, 2014, 2:21:28 PM4/4/14
to sna...@googlegroups.com
Thanks! That's what I ended up doing, asking the designers to set up the icons this way. If it's ever desirable to see the icons all together I can always create a simple app to do that and it can have more flexibility, i.e, pick a subset of icons and show them side be side or in a group.
Reply all
Reply to author
Forward
0 new messages