Skip to content

Instantly share code, notes, and snippets.

@michael
Created April 22, 2012 05:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save michael/2458819 to your computer and use it in GitHub Desktop.
Save michael/2458819 to your computer and use it in GitHub Desktop.
The Scatterplot Dance

Dance.js is dancing based on data. It's much like Backbone.js, but with a foundation for building interactive visualizations in the spirit of D3.js. It comes with Data.js, a uniform interface for handling your domain data.

window.countries_data = {
"type": {
"_id": "/type/country",
"name": "Countries",
"properties": {
"name": {"name": "Country Name", "type": "string" },
"languages": {"name": "Languages spoken", "type": "string" },
"population": { "name": "Population", "type": "number" },
"gdp": { "name": "GDP per capita", "type": "number" }
},
"indexes": {
"by_name": ["name"]
}
},
"objects": [
{
"_id": "at",
"name": "Austria",
"languages": ["German", "Austrian"],
"population": 8.3,
"gdp": 41.805
},
{
"_id": "de",
"name": "Germany",
"languages": ["German"],
"population": 82,
"gdp": 46.860
},
{
"_id": "us",
"name": "United States of America",
"languages": ["German", "English", "Spanish", "Chinese", "French"],
"population": 311,
"gdp": 36.081
},
{
"_id": "uk",
"name": "United Kingdom",
"languages": ["English", "Irish", "Scottish Gaelic"],
"population": 62.3,
"gdp": 36.081
},
{
"_id": "es",
"name": "Spain",
"languages": ["Spanish"],
"population": 30.6,
"gdp": 36.081
},
{
"_id": "gr",
"name": "Greece",
"languages": ["Greek"],
"population": 11.0,
"gdp": 36.081
},
{
"_id": "ca",
"name": "Canada",
"languages": ["English", "French", "Spanish"],
"population": 40.1,
"gdp": 40.457
}
]
};
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'/>
<title>Dance.js - The Scatterplot Dance</title>
<link rel='stylesheet' href='scatterplot.css'>
<script src='https://raw.github.com/documentcloud/underscore/87cac5bd057ceafd6f779b1df33de61ca21b5e1d/underscore.js'></script>
<script src='https://raw.github.com/michael/data/fe65cb9ab32fbee59f14f44e17e186cf69ff16a7/data.js'></script>
<script src='http://code.jquery.com/jquery-1.7.2.min.js'></script>
<script src='https://raw.github.com/michael/dance/96cb9a6384acce19202275c6dce9b7fbdac87763/dance.js'></script>
<!-- Countries data -->
<script src='countries.js'></script>
<!-- Dance Performers -->
<script src="scatterplot.js"></script>
<script>
$(function() {
window.countries = new Data.Collection(countries_data);
window.scatterplot = new Scatterplot({});
scatterplot.update(countries, ["gdp", "population"]);
// Update
function update() {
var language = $('#language').val();
var query = {};
if (language) query["languages"] = [ language ];
var items = countries.find(query);
scatterplot.update(items, [$('#property_x').val(), $('#property_y').val()]);
}
$('#property_x').change(update);
$('#property_y').change(update);
$('#language').change(update);
});
</script>
</head>
<body>
<div id='container'>
X
<select id="property_x">
<option value="gdp">GDP (thousands)</option>
<option value="population">Population (millions)</option>
</select>
Y
<select id="property_y">
<option value="gdp">GDP (thousands)</option>
<option selected="selected" value="population">Population (millions)</option>
</select>
<select id="language">
<option value="">All</option>
<option value="English">English</option>
<option value="French">French</option>
<option value="German">German</option>
<option value="Greek">Greek</option>
<option value="Spanish">Spanish</option>
<option value="Scottish Gaelic">Scottish Gaelic</option>
</select>
<div id='canvas'></div>
</div>
</body>
</html>
body {
font: 15px 'Helvetica Neue' Arial
}
#canvas {
margin-top: 20px;
border: 30px solid #eee;
width: 550px;
height: 350px;
background: #eee;
position: relative;
}
.dot {
-moz-transition-duration: 0.8s;
-webkit-transition-duration: 0.8s;
transition-duration: 0.8s;
position: absolute;
bottom: 0px;
background: steelblue;
opacity: 0.7;
border-radius: 5px;
}
.bar:hover { opacity: 1.0; }
.dot .label {
display: none;
text-transform: uppercase;
position: absolute;
bottom: -25px;
left: 10px;
right: 0;
width: 40px;
text-align: center;
background: #ccc;
padding: 10px;
}
.dot:hover .label {
display: block;
}
.dot .value {
font-weight: bold;
text-transform: uppercase;
position: absolute;
top: -25px;
left: 0;
right: 0;
text-align: center;
}
(function(exports) {
// Helpers
// ------------
function htmlId(obj) {
return obj._id.split('/').join('_');
}
// Collections you wanna dance with
// ------------
var collections = {
"items": {
enter: function(items) {
items.each(function(item) {
var dot = $('<div class="dot" id="'+htmlId(item)+'"><div class="label">'+item._id+'</div></div>')
.css('left', Math.random()*$('#canvas').width())
.css('bottom', Math.random()*$('#canvas').height())
.css('width', 1)
.css('height', 1);
$('#canvas').append(dot);
});
// Delegate to update (motion tweening fun)
_.delay(this.collections["items"].update, 200, items);
},
update: function(items) {
items.each(function(item) {
var cell = $('#'+htmlId(item))
.css('left', item.pos.x)
.css('bottom', item.pos.y)
.css('width', 10)
.css('height', 10);
});
},
exit: function(items) {
items.each(function(i) { $('#'+htmlId(i)).remove() });
}
}
};
// Scatterplot Visualization
// ------------
var Scatterplot = Dance.Performer.extend({
collections: collections,
initialize: function(options) {
this.data["items"] = options.items;
},
layout: function(properties) {
var that = this;
// Prepare scales
function aggregate(p, fn) {
var values = _.map(that.data["items"].objects, function(i) { return i.get(p); });
return fn.apply(this, values);
}
var minX = aggregate(properties[0], Math.min);
var maxX = aggregate(properties[0], Math.max);
var minY = aggregate(properties[1], Math.min);
var maxY = aggregate(properties[1], Math.max);
function x(val) {
return (((val-minX) * $('#canvas').width()) / (maxX-minX));
}
function y(val) {
return (((val-minY) * $('#canvas').height()) / (maxY-minY));
}
// Apply layout
this.data["items"].each(function(item, key, index) {
item.pos = {
x: x(item.get(properties[0])),
y: y(item.get(properties[1]))
};
});
},
update: function(items, properties) {
this.data["items"] = items;
this.layout(properties);
this.refresh();
}
});
exports.Scatterplot = Scatterplot;
})(window);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment