|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<style> |
|
.container { |
|
max-width: 1270px; |
|
margin: 0 auto; |
|
} |
|
.graphic { |
|
max-width: 1000px; |
|
display: block; |
|
margin: 25px auto; |
|
} |
|
.infobox { |
|
display: none; |
|
} |
|
</style> |
|
<body> |
|
<article class="container"> |
|
<section class="graphic"> |
|
<section class="treemap"></section> |
|
<section class="infobox"></section> |
|
</section> |
|
</article> |
|
</body> |
|
<script src="//d1qqc1e9kvmdh8.cloudfront.net/js/jquery-1.12.4/jquery.min.js"></script> |
|
<script src="//d1qqc1e9kvmdh8.cloudfront.net/js/underscore-1.8.3-min.js"></script> |
|
<script src="//d3js.org/d3.v3.min.js"></script> |
|
<script src="//d3js.org/queue.v1.min.js"></script> |
|
<script> |
|
var app = {}; |
|
app.selector = "section.graphic"; |
|
app.$el = $(app.selector); |
|
app.$infobox = $(".infobox", app.selector); |
|
|
|
d3.selection.prototype.moveToFront = function() { |
|
return this.each(function(){ |
|
this.parentNode.appendChild(this); |
|
}); |
|
}; |
|
|
|
app.prep_csv_row = function (row) { |
|
return { |
|
name: row.name, |
|
last_name: row.last_name, |
|
salary: +row.salary, |
|
team: row.team |
|
} |
|
}; |
|
|
|
app.intcomma = function(x) { |
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
|
}; |
|
|
|
app.draw = function () { |
|
console.log("draw"); |
|
// Calculate the height and width |
|
app.width = app.$el.width(); |
|
// ... once we're below a mobile cutoff width, the treemap becomes a square |
|
if (app.width < 580) { |
|
app.isMobile = true; |
|
app.scaledHeight = app.width * 1; |
|
// otherwise it's more of a rectangle |
|
} else { |
|
app.isMobile = false; |
|
app.scaledHeight = app.width * 0.45; |
|
} |
|
// Create the d3 layout |
|
app.chart = d3.layout.treemap() |
|
.size([app.width, app.scaledHeight]) |
|
.sticky(true) |
|
.sort(function(x, y){ |
|
return d3.ascending(x.salary, y.salary); |
|
}) |
|
.value(function(d) { return d.salary; }); |
|
// Create the svg |
|
app.svg = d3.select("section.treemap") |
|
.append("svg") |
|
.attr("width", app.width) |
|
.attr("height", app.scaledHeight); |
|
// Create g elements to group all the squares with their labels |
|
app.nodes = app.svg.datum(app.clustered_data).selectAll("g") |
|
.data(app.chart.nodes) |
|
.enter() |
|
.append("g") |
|
.attr("data-name", function(d) { return d.name; }) |
|
.attr("data-last-name", function(d) { return d.last_name; }) |
|
.attr("data-team", function (d) { return d.team; }) |
|
.attr("data-salary", function(d) { return d.salary; }); |
|
// Create the squares inside those |
|
app.nodes.append("rect") |
|
.attr("x", function(d) { return d.x; }) |
|
.attr("y", function(d) { return d.y; }) |
|
.attr("width", function(d) { return d.dx; }) |
|
.attr("height", function(d) { return d.dy; }) |
|
.attr("stroke", "white") |
|
.attr("stroke-width", 0.5) |
|
.attr("fill", function (d) { |
|
return d.team == 'Dodgers' ? '#005A9C': '#EB6E1F'; |
|
}); |
|
// Add the labels as well |
|
app.nodes.append('text') |
|
.attr("x", function(d) { return d.x + 5; }) |
|
.attr("y", function(d) { return d.y + 20; }) |
|
.text(function (d) { |
|
// Only if there are more than 5 million, to avoid labels on very small boxes |
|
if ((!app.isMobile) & d.salary > 5000000) { |
|
return d.last_name; |
|
} else if (d.salary > 10000000) { |
|
return d.last_name |
|
} |
|
}) |
|
.attr("font-family", "Courier") |
|
.attr("font-size", function (d) { return app.isMobile == true ? '13px': '16px'; }) |
|
.attr("text-anchor", "left"); |
|
|
|
app.nodes.on("mouseover", app.mouseover); |
|
app.nodes.on("mouseout", app.mouseout); |
|
|
|
}; |
|
|
|
app.resize = function () { |
|
console.log("resize"); |
|
$("section.treemap").empty(); |
|
app.draw(); |
|
}; |
|
|
|
app.mouseover = function() { |
|
d3.select(this) |
|
.select("rect") |
|
.attr("stroke-width", 2); |
|
d3.select(this).moveToFront(); |
|
var that = $(this); |
|
var html = "<b>" + that.attr("data-name") + "</b> $" + app.intcomma(that.attr("data-salary")); |
|
app.$infobox.html(html).show(); |
|
}; |
|
|
|
app.mouseout = function () { |
|
d3.select(this) |
|
.select("rect") |
|
.attr("stroke-width", 0.3); |
|
app.$infobox.html("").hide(); |
|
} |
|
|
|
app.boot = function (error, data) { |
|
console.log("boot"); |
|
app.data = _.map(data, app.prep_csv_row); |
|
app.clustered_data = { |
|
"name": "cluster", |
|
"children": [ |
|
{ |
|
"name": "dodgers", |
|
"children":_.where(app.data, {"team": "Dodgers"}) |
|
}, |
|
{ |
|
"name": "astros", |
|
"children":_.where(app.data, {"team": "Astros"}) |
|
}, |
|
] |
|
}; |
|
app.draw(); |
|
d3.select(window).on("resize", app.resize); |
|
}; |
|
|
|
queue() |
|
.defer(d3.csv, 'players.csv') |
|
.await(app.boot); |
|
|
|
</script> |