Last active
August 29, 2015 14:07
-
-
Save nitaku/10718152ec982ab3bca7 to your computer and use it in GitHub Desktop.
(Almost) stable word cloud
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# layout, behaviors and scales | |
SCALE = 400 | |
treemap = d3.layout.treemap() | |
.size([SCALE*1.3, SCALE]) | |
.value((node) -> node.size) | |
correct_x = d3.scale.linear() | |
.domain([0, SCALE]) | |
.range([0, 420]) | |
correct_y = d3.scale.linear() | |
.domain([0, SCALE]) | |
.range([0, 300]) | |
color = (txt, light) -> | |
Math.seedrandom(txt+'abcdef') | |
noise = (W) -> Math.random()*W - W/2 | |
d3.hcl(0+noise(360), 20, if light then 65 else 25) | |
svg = d3.select('svg') | |
width = svg.node().getBoundingClientRect().width | |
height = svg.node().getBoundingClientRect().height | |
# translate the viewBox to have (0,0) at the center of the vis | |
svg | |
.attr | |
viewBox: "#{-width/2} #{-height/2} #{width} #{height}" | |
# append a group for zoomable content | |
zoomable_layer = svg.append('g') | |
# define a zoom behavior | |
zoom = d3.behavior.zoom() | |
.scaleExtent([1,10]) # min-max zoom | |
.on 'zoom', () -> | |
# GEOMETRIC ZOOM | |
zoomable_layer | |
.attr | |
transform: "translate(#{zoom.translate()})scale(#{zoom.scale()})" | |
# bind the zoom behavior to the main SVG | |
svg.call(zoom) | |
# group the visualization | |
vis = zoomable_layer.append('g') | |
.attr | |
transform: "translate(#{-SCALE*1.3/2},#{-SCALE/2})" | |
scores = [7, 6, 5, 4, 3, 2, 1] | |
redraw = (duration) -> | |
scores[Math.floor(Math.random()*7)] += Math.random()*3 | |
tree = { | |
children: [ | |
{name: 'alpha', size: scores[0]}, | |
{name: 'beta', size: scores[1]}, | |
{name: 'gamma', size: scores[2]}, | |
{name: 'delta', size: scores[3]}, | |
{name: 'epsilon', size: scores[4]}, | |
{name: 'zeta', size: scores[5]}, | |
{name: 'eta', size: scores[6]} | |
] | |
} | |
nodes_data = treemap.nodes(tree) | |
#nodes = vis.selectAll('.node') | |
# .data(nodes_data.filter((node) -> node.depth is 1)) | |
# | |
#enter_nodes = nodes.enter().append('rect') | |
# .attr | |
# class: 'node' | |
# x: (node) -> node.x | |
# y: (node) -> node.y | |
# width: (node) -> node.dx | |
# height: (node) -> node.dy | |
# fill: (node) -> color(node.name, true) | |
labels = vis.selectAll('.label') | |
.data(nodes_data.filter((node) -> node.depth is 1)) | |
enter_labels = labels.enter().append('svg') | |
.attr | |
class: 'label' | |
preserveAspectRatio: 'none' | |
enter_labels.append('text') | |
.text((node) -> node.name.toUpperCase()) | |
.attr | |
dy: '0.35em' | |
fill: (node) -> color(node.name, false) | |
labels.select('text').each (node) -> | |
bbox = this.getBBox() | |
bbox_aspect = bbox.width / bbox.height | |
node_bbox = {width: node.dx, height: node.dy} | |
node_bbox_aspect = node_bbox.width / node_bbox.height | |
node.rotate = bbox_aspect >= 1 and node_bbox_aspect < 1 or bbox_aspect < 1 and node_bbox_aspect >= 1 | |
node.label_bbox = { | |
x: bbox.x+(bbox.width-correct_x(bbox.width))/2, | |
y: bbox.y+(bbox.height-correct_y(bbox.height))/2, | |
width: correct_x(bbox.width), | |
height: correct_y(bbox.height) | |
} | |
if node.rotate | |
node.label_bbox = { | |
x: node.label_bbox.y, | |
y: node.label_bbox.x, | |
width: node.label_bbox.height, | |
height: node.label_bbox.width | |
} | |
labels.transition().duration(duration) | |
.attr | |
x: (node) -> node.x | |
y: (node) -> node.y | |
width: (node) -> node.dx | |
height: (node) -> node.dy | |
viewBox: (node) -> "#{node.label_bbox.x} #{node.label_bbox.y} #{node.label_bbox.width} #{node.label_bbox.height}" | |
.select('text') | |
.attr | |
transform: (node) -> if node.rotate then 'rotate(-90)' else 'rotate(0)' | |
setInterval((() -> redraw(1200)), 2000) | |
redraw(0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
svg { | |
background: white; | |
} | |
.node { | |
shape-rendering: crispEdges; | |
vector-effect: non-scaling-stroke; | |
stroke: white; | |
stroke-width: 2; | |
} | |
.label { | |
pointer-events: none; | |
text-anchor: middle; | |
font-family: Impact; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="description" content="(Almost) stable word cloud" /> | |
<title>(Almost) stable word cloud</title> | |
<link type="text/css" href="index.css" rel="stylesheet"/> | |
<script src="http://davidbau.com/encode/seedrandom-min.js"></script> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
</head> | |
<body> | |
<svg height="500" width="960"></svg> | |
<script src="index.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
var SCALE, color, correct_x, correct_y, height, redraw, scores, svg, treemap, vis, width, zoom, zoomable_layer; | |
SCALE = 400; | |
treemap = d3.layout.treemap().size([SCALE * 1.3, SCALE]).value(function(node) { | |
return node.size; | |
}); | |
correct_x = d3.scale.linear().domain([0, SCALE]).range([0, 420]); | |
correct_y = d3.scale.linear().domain([0, SCALE]).range([0, 300]); | |
color = function(txt, light) { | |
var noise; | |
Math.seedrandom(txt + 'abcdef'); | |
noise = function(W) { | |
return Math.random() * W - W / 2; | |
}; | |
return d3.hcl(0 + noise(360), 20, light ? 65 : 25); | |
}; | |
svg = d3.select('svg'); | |
width = svg.node().getBoundingClientRect().width; | |
height = svg.node().getBoundingClientRect().height; | |
svg.attr({ | |
viewBox: "" + (-width / 2) + " " + (-height / 2) + " " + width + " " + height | |
}); | |
zoomable_layer = svg.append('g'); | |
zoom = d3.behavior.zoom().scaleExtent([1, 10]).on('zoom', function() { | |
return zoomable_layer.attr({ | |
transform: "translate(" + (zoom.translate()) + ")scale(" + (zoom.scale()) + ")" | |
}); | |
}); | |
svg.call(zoom); | |
vis = zoomable_layer.append('g').attr({ | |
transform: "translate(" + (-SCALE*1.3 / 2) + "," + (-SCALE / 2) + ")" | |
}); | |
scores = [7, 6, 5, 4, 3, 2, 1]; | |
redraw = function(duration) { | |
var enter_labels, labels, nodes_data, tree; | |
scores[Math.floor(Math.random() * 7)] += Math.random() * 3; | |
tree = { | |
children: [ | |
{ | |
name: 'alpha', | |
size: scores[0] | |
}, { | |
name: 'beta', | |
size: scores[1] | |
}, { | |
name: 'gamma', | |
size: scores[2] | |
}, { | |
name: 'delta', | |
size: scores[3] | |
}, { | |
name: 'epsilon', | |
size: scores[4] | |
}, { | |
name: 'zeta', | |
size: scores[5] | |
}, { | |
name: 'eta', | |
size: scores[6] | |
} | |
] | |
}; | |
nodes_data = treemap.nodes(tree); | |
labels = vis.selectAll('.label').data(nodes_data.filter(function(node) { | |
return node.depth === 1; | |
})); | |
enter_labels = labels.enter().append('svg').attr({ | |
"class": 'label', | |
preserveAspectRatio: 'none' | |
}); | |
enter_labels.append('text').text(function(node) { | |
return node.name.toUpperCase(); | |
}).attr({ | |
dy: '0.35em', | |
fill: function(node) { | |
return color(node.name, false); | |
} | |
}); | |
labels.select('text').each(function(node) { | |
var bbox, bbox_aspect, node_bbox, node_bbox_aspect; | |
bbox = this.getBBox(); | |
bbox_aspect = bbox.width / bbox.height; | |
node_bbox = { | |
width: node.dx, | |
height: node.dy | |
}; | |
node_bbox_aspect = node_bbox.width / node_bbox.height; | |
node.rotate = bbox_aspect >= 1 && node_bbox_aspect < 1 || bbox_aspect < 1 && node_bbox_aspect >= 1; | |
node.label_bbox = { | |
x: bbox.x + (bbox.width - correct_x(bbox.width)) / 2, | |
y: bbox.y + (bbox.height - correct_y(bbox.height)) / 2, | |
width: correct_x(bbox.width), | |
height: correct_y(bbox.height) | |
}; | |
if (node.rotate) { | |
return node.label_bbox = { | |
x: node.label_bbox.y, | |
y: node.label_bbox.x, | |
width: node.label_bbox.height, | |
height: node.label_bbox.width | |
}; | |
} | |
}); | |
return labels.transition().duration(duration).attr({ | |
x: function(node) { | |
return node.x; | |
}, | |
y: function(node) { | |
return node.y; | |
}, | |
width: function(node) { | |
return node.dx; | |
}, | |
height: function(node) { | |
return node.dy; | |
}, | |
viewBox: function(node) { | |
return "" + node.label_bbox.x + " " + node.label_bbox.y + " " + node.label_bbox.width + " " + node.label_bbox.height; | |
} | |
}).select('text').attr({ | |
transform: function(node) { | |
if (node.rotate) { | |
return 'rotate(-90)'; | |
} else { | |
return 'rotate(0)'; | |
} | |
} | |
}); | |
}; | |
setInterval((function() { | |
return redraw(1200); | |
}), 2000); | |
redraw(0); | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment