Last active
August 29, 2015 13:57
-
-
Save danharr/9834127 to your computer and use it in GitHub Desktop.
Swimlanes for media plans
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 http-equiv="Content-Type" content="text/html;charset=utf-8"> | |
<title>Media Plan</title> | |
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.js"></script> | |
<style type="text/css"> | |
@font-face{font-family:danx;src:url('http://www.24-the-movie.com/dandelion in the spring.ttf')} | |
h1 { | |
font-size: 22px; | |
font-family:'lucida sans',verdana, geneva, lucida, 'lucida grande', arial, helvetica, sans-serif; | |
display: inline; | |
margin-left:200px; | |
color:#4169E1; | |
} | |
p { | |
font-size: 11px; | |
font-family:'lucida sans',verdana, geneva, lucida, 'lucida grande', arial, helvetica, sans-serif; | |
margin-left:200px; | |
} | |
.chart { | |
shape-rendering: crispEdges; | |
} | |
.mini text { | |
font: 9px 'lucida sans',tahoma,sans-serif; | |
} | |
.main text { | |
font: 12px 'lucida sans',tahoma,sans-serif; | |
} | |
.miniItem0 { | |
fill: lawngreen; | |
stroke-width: 6; | |
} | |
.miniItem1 { | |
fill: magenta; | |
fill-opacity: .7; | |
stroke-width: 6; | |
} | |
.miniItem2 { | |
fill: dodgerblue; | |
fill-opacity: .7; | |
stroke-width: 6; | |
} | |
.miniItem3 { | |
fill: gold; | |
fill-opacity: .7; | |
stroke-width: 6; | |
} | |
.miniItem4 { | |
fill: skyblue; | |
fill-opacity: .7; | |
stroke-width: 6; | |
} | |
.miniItem5 { | |
fill: yellowgreen; | |
fill-opacity: .7; | |
stroke-width: 6; | |
} | |
.miniItem6 { | |
fill: deepskyblue; | |
fill-opacity: .7; | |
stroke-width: 6; | |
} | |
.miniItem7 { | |
fill: pink; | |
fill-opacity: .7; | |
stroke-width: 6; | |
} | |
.brush .extent { | |
stroke: gray; | |
fill: dodgerblue; | |
fill-opacity: .365; | |
} | |
</style> | |
</head> | |
<body> | |
<h1 >Proposed Media Plan</h1> | |
<p>The below visual shows a proposed media plan. Use the small chart at the very point (that shows everything) to make zoomed in selections on the top visual. <b>This won't work in Internet Explorer versions 8 and below.</b><em> Click on the mini visual at the bottom to get started...</em></p> | |
<script type="text/javascript"> | |
//data | |
var lanes = ["TV","Press","OOH","Radio","Digital","Mobile","Cinema","VoD"], | |
laneLength = lanes.length, | |
items = [{"lane": 0, "id": "£16m", "start": 2, "end": 25}, | |
{"lane": 1, "id": "£566k", "start": 5, "end": 7}, | |
{"lane": 2, "id": "£386k", "start": 4, "end": 7}, | |
{"lane": 3, "id": "£4.4m", "start": 2, "end": 25}, | |
{"lane": 4, "id": "£1.9m", "start": 2, "end": 25}, | |
{"lane": 5, "id": "£506k", "start": 2, "end": 25}, | |
{"lane": 6, "id": "£254k", "start": 4, "end": 5}, | |
{"lane": 7, "id": "£819k", "start": 2, "end": 25} | |
] | |
timeBegin = 0, | |
timeEnd = 25; | |
</script> | |
<script type="text/javascript"> | |
var m = [50, 15, 15, 200], //top right bottom left | |
w = 1100 - m[1] - m[3], | |
h = 700 - m[0] - m[2], | |
miniHeight = laneLength * 12 + 50, | |
mainHeight = h - miniHeight - 50; | |
padding = 10; | |
//scales | |
var x = d3.scale.linear() | |
.domain([timeBegin, timeEnd]) | |
.range([0, w]); | |
var x1 = d3.scale.linear() | |
.range([0, w]); | |
var y1 = d3.scale.linear() | |
.domain([0, laneLength]) | |
.range([0, mainHeight]); | |
var y2 = d3.scale.linear() | |
.domain([0, laneLength]) | |
.range([0, miniHeight]); | |
var chart = d3.select("body") | |
.append("svg") | |
.attr("width", w + m[1] + m[3]) | |
.attr("height", h + m[0] + m[2]) | |
.attr("class", "chart"); | |
chart.append("defs").append("clipPath") | |
.attr("id", "clip") | |
.append("rect") | |
.attr("width", w) | |
.attr("height", mainHeight); | |
var main = chart.append("g") | |
.attr("transform", "translate(" + m[3] + "," + m[0] + ")") | |
.attr("width", w) | |
.attr("height", mainHeight) | |
.attr("class", "main"); | |
var mini = chart.append("g") | |
.attr("transform", "translate(" + m[3] + "," + (mainHeight + m[0]) + ")") | |
.attr("width", w) | |
.attr("height", miniHeight) | |
.attr("class", "mini"); | |
//main lanes and texts | |
main.append("g").selectAll(".laneLines") | |
.data(items) | |
.enter().append("line") | |
.attr("x1", m[1]) | |
.attr("y1", function(d) {return y1(d.lane);}) | |
.attr("x2", w) | |
.attr("y2", function(d) {return y1(d.lane);}) | |
.attr("stroke", "lightgray") | |
main.append("g").selectAll(".laneText") | |
.data(lanes) | |
.enter().append("text") | |
.text(function(d) {return d;}) | |
.attr("x", -m[1]) | |
.attr("y", function(d, i) {return y1(i + .5);}) | |
.attr("dy", ".5ex") | |
.attr("text-anchor", "end") | |
.attr("class", "laneText"); | |
//mini lanes and texts | |
mini.append("g").selectAll(".laneLines") | |
.data(items) | |
.enter().append("line") | |
.attr("x1", m[1]) | |
.attr("y1", function(d) {return y2(d.lane);}) | |
.attr("x2", w) | |
.attr("y2", function(d) {return y2(d.lane);}) | |
.attr("stroke", "lightgray"); | |
mini.append("g").selectAll(".laneText") | |
.data(lanes) | |
.enter().append("text") | |
.text(function(d) {return d;}) | |
.attr("x", -m[1]) | |
.attr("y", function(d, i) {return y2(i + .5);}) | |
.attr("dy", ".5ex") | |
.attr("text-anchor", "end") | |
.attr("class", "laneText"); | |
var itemRects = main.append("g") | |
.attr("clip-path", "url(#clip)"); | |
//mini item rects | |
mini.append("g").selectAll("miniItems") | |
.data(items) | |
.enter().append("rect") | |
.attr("class", function(d) {return "miniItem" + d.lane;}) | |
.attr("x", function(d) {return x(d.start);}) | |
.attr("y", function(d) {return y2(d.lane + .5) - 5;}) | |
.attr("width", function(d) {return x(d.end - d.start);}) | |
.attr("height", 10); | |
//mini labels | |
mini.append("g").selectAll(".miniLabels") | |
.data(items) | |
.enter().append("text") | |
.text(function(d) {return d.id;}) | |
.attr("x", function(d) {return x(d.start);}) | |
.attr("y", function(d) {return y2(d.lane + .5);}) | |
.attr("dy", ".5ex"); | |
//brush | |
var brush = d3.svg.brush() | |
.x(x) | |
.on("brush", display); | |
mini.append("g") | |
.attr("class", "x brush") | |
.call(brush) | |
.selectAll("rect") | |
.attr("y", 1) | |
.attr("height", miniHeight - 1); | |
display(); | |
function display() { | |
var rects, labels, | |
minExtent = brush.extent()[0], | |
maxExtent = brush.extent()[1], | |
visItems = items.filter(function(d) {return d.start < maxExtent && d.end > minExtent;}); | |
mini.select(".brush") | |
.call(brush.extent([minExtent, maxExtent])); | |
x1.domain([minExtent, maxExtent]); | |
//update main item rects | |
rects = itemRects.selectAll("rect") | |
.data(visItems, function(d) { return d.id; }) | |
.attr("x", function(d) {return x1(d.start);}) | |
.attr("width", function(d) {return x1(d.end) - x1(d.start);}); | |
rects.enter().append("rect") | |
.attr("class", function(d) {return "miniItem" + d.lane;}) | |
.attr("x", function(d) {return x1(d.start);}) | |
.attr("y", function(d) {return y1(d.lane) + 10;}) | |
.attr("width", function(d) {return x1(d.end) - x1(d.start);}) | |
.attr("height", function(d) {return .8 * y1(1);}); | |
rects.exit().remove(); | |
//update the item labels | |
labels = itemRects.selectAll("text") | |
.data(visItems, function (d) { return d.id; }) | |
.attr("x", function(d) {return x1(Math.max(d.start, minExtent) + 2);}); | |
labels.enter().append("text") | |
.text(function(d) {return d.id;}) | |
.attr("x", function(d) {return x1(Math.max(d.start, minExtent));}) | |
.attr("y", function(d) {return y1(d.lane + .5);}) | |
.attr("text-anchor", "start"); | |
labels.exit().remove(); | |
//Define X axis | |
var xAxis = d3.svg.axis() | |
.scale(x1) | |
.orient("bottom"); | |
//Create X axis | |
svg.append("g") | |
.call(xAxis); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment