Skip to content

Instantly share code, notes, and snippets.

@nsonnad
Created June 13, 2013 12:00
Show Gist options
  • Save nsonnad/5773144 to your computer and use it in GitHub Desktop.
Save nsonnad/5773144 to your computer and use it in GitHub Desktop.
d3.sticker
{"description":"d3.sticker","endpoint":"","display":"svg","public":true,"require":[],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"sticker.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"icon.svg":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"fullscreen":false,"play":false,"loop":false,"restart":false,"autoinit":true,"pause":true,"loop_type":"period","bv":false,"nclones":15,"clone_opacity":0.4,"duration":3000,"ease":"linear","dt":0.01}
Display the source blob
Display the rendered blob
Raw
<g display="none">
<g id="smiley">
<circle r=44 opacity=0.94 fill=#E1E928 class=face></circle>
<ellipse rx=5 ry=10 cx=-10 cy=-5></ellipse>
<ellipse rx=5 ry=10 cx=10 cy=-5></ellipse>
<path fill=none stroke=#000000 stroke-width=5 transform="translate(0,10)"
d="M-25,0C-25,0,-25,0,-21,3C-16,7,-8,15,0,15C8,15,16,7,21,3C25,0,25,0,25,0"></path>
</g>
</g>
//data driven stickers
var svg = d3.select("svg");
//this creates a sticker from an existing DOM element
var sticker = d3.sticker("#smiley");
console.log("sticker:", sticker)
var data = d3.range(16);
var selection = svg.selectAll("g.sticker")
.data(data)
.enter()
.append("g")
.attr("transform", function(d,i) {
var x = 137 + (i % 4) * 120;
var y = 100 + Math.floor(i / 4) * 110;
return "translate(" + [x,y] + ")";
})
//you can create stickers this way:
//selection.call(sticker)
var color = d3.scale.category20();
//or this way:
sticker(selection)
.select(".face")
.attr("fill", function(d,i) { return color(d) })
//you can also insert them with similar syntax to d3
sticker.insert(selection, "g")
.attr("transform", "translate(-47,-10)")
(function() {
d3.sticker = function(selector) {
var string;
var node;
var svgElement; //for deserializing svg elements
var sticker = function(selection) {
return sticker.append(selection);
}
sticker.copy = function(selector) {
node = d3.select(selector).node();
if(!node) return sticker;
//we keep track of svg element
if(d3_isSVG(node)) {
sticker.isSVG = true;
svgElement = node.ownerSVGElement;
}
node = node.cloneNode(true);
node.removeAttribute("id");
return sticker;
}
sticker.paste = function() {
if(!node) return;
return node.cloneNode(true);
}
sticker.node = function(_) {
if(!arguments.length) return node;
node = _;
if(d3_isSVG(node)) {
sticker.isSVG = true;
svgElement = node.ownerSVGElement;
}
return sticker;
}
//append a copy of the sticker to the selection
sticker.append = function(selection) {
return selection.select(function() {
return this.appendChild(sticker.paste());
});
}
//insert a copy of the sticker into a selection similar to the d3 insert API
sticker.insert = function(selection, before) {
if(!string) return selection;
return selection.select(before).select(function() {
return this.parentNode.insertBefore(sticker.paste(), this);
});
}
sticker.string = function(_) {
if(!arguments.length) return string;
string = _;
return sticker;
}
sticker.serialize = function() {
//Serialize the selected element into a string
string = new XMLSerializer().serializeToString(node);
}
sticker.deserialize = function () {
//check if our element is SVG
if(sticker.isSVG) {
node = d3_makeSVGFragment(string, svgElement);
} else {
node = d3_makeFragment(string);
}
return node;
}
sticker.toString = function() {
sticker.serialize();
return string;
}
if(selector) {
return sticker.copy(selector);
}
return sticker;
}
function d3_isSVG(el) {
if(!el) return false
return !!el.ownerSVGElement;// || el.tagName === "svg";
}
function d3_makeFragment(fragment) {
var range = document.createRange()
return range.createContextualFragment(fragment);
}
function d3_makeSVGFragment(fragment, svgElement) {
//we need to wrap our element in a temporarary intermediate svg element
//so that the browser knows to instanciate the Node properly.
//for some reason having the range select an svg element isn't enough.
// TODO: Allow optional namespace declarations
var pre = '<svg xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink>';
var post = '</svg>';
var range = document.createRange();
range.selectNode(svgElement);
var contextFragment = range.createContextualFragment(pre + fragment + post)
var intermediateSvg = contextFragment.childNodes[0]
var node = intermediateSvg.childNodes[0]
return node;
}
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment