Built with blockbuilder.org
xxxxxxxxxx
<head>
<link href='https://fonts.googleapis.com/css?family=Cabin:300,400' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Lato:300,400' rel='stylesheet' type='text/css'>
<link rel="shortcut icon" href="https://samhooker.com/images/initials/shinitfavicon.png" type="image/jpeg" />
<title>Zack Hample Countdown</title>
</head>
<style>
body {
background: BlanchedAlmond;
}
.centerme {
display: block;
margin: auto;
}
svg {
display: block;
margin: auto;
margin-top: 25px;
}
h1 {
font-family: Cabin;
text-align: center;
margin-top: 10px;
margin-left: 60px;
margin-right: 60px;
color: DarkRed;
}
h3 {
font-family: Lato;
margin-left: 80px;
margin-right: 80px;
text-align: center;
margin-top: -8px;
color: DarkRed;
}
h5 {
font-family: Lato;
font-style: italic;
text-align:center;
line-height: 1.0em;
margin-top: -7px;
color: DarkRed;
text-decoration: none;
}
a, a:visited, a:active {
color: Ivory;
}
.button {
min-width: 130px;
padding: 4px 5px;
cursor: pointer;
margin: auto;
font-size: 13px;
border: 1px solid DarkGoldenrod;
color: Tomato;
text-decoration: none;
font-family: Lato;
display: inline-block;
float: center;
margin: 3px;
border-radius: 8px;
background-color: Ivory;
}
.button.active {
background: Tomato;
color: Ivory;
}
#vis {
clear: both;
margin-bottom: 10px;
display: block;
margin: auto;
}
#toolbar {
font-family: Lato;
color: LightSteelBlue;
display: block;
margin: auto;
position: relative;
width: 820px;
text-align: center;
padding-left: 20px;
margin-top: -10px;
}
.year {
font-size: 21px;
fill: #aaa;
cursor: default;
}
.title {
font-size: 18px;
fill: BlanchedAlmond;
cursor: default;
}
.tooltip .title {
clear: both;
font-size: 16px;
color: Ivory;
opacity: 1.0;
}
.tooltip .gameday {
clear: both;
font-size: 14px;
color: Ivory;
cursor: default;
opacity: 1.0;
}
.tooltip {
background-color: Crimson;
padding: .5em;
font-family: Lato;
font-size: 12px;
color: #fff;
font-weight: bolder;
border-radius: 6px;
border: 1px solid Ivory;
opacity: 1;
position: absolute;
text-align: center;
margin-top: 20px;
line-height: 1.3em;
box-shadow: 4px 4px 12px rgba(0,0,0,.5);
}
#init {
clear: both;
display: visible;
position: absolute;
padding: 5px;
z-index: 1;
margin-left: 8px;
}
#init2 {
position: absolute;
padding: 7px;
margin-left: 8px;
}
.bubbles {
box-shadow: 2px;
color: black;
}
</style>
<body>
<div class="container">
<a href="https://samhooker.com" target="_blank"><img id="init" src="https://samhooker.com/images/initials/shinitindianred.png" width="35">
<img id="init2" src="https://samhooker.com/images/initials/shinitgainsboro.png" width="35"></a>
<h1>Countdown to 10,000</h1>
<h5><i>UPDATED: </i>7/3/17</h5>
<div id="toolbar">
<a href="#" id="all" class="button active">All Games (1993-2017)</a>
<a href="#" id="year" class="button">By Year</a>
<a href="#" id="stadiumb" class="button">By Stadium</a>
</div>
<div id="vis"></div>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.24.0/d3-legend.min.js"></script>
<script>
function bubbleChart() {
// Constants for sizing
var width = 1200;
var height = 960;
var tooltip = floatingTooltip('gates_tooltip', 120);
// Locations to move bubbles towards, depending
// on which view mode is selected.
var center = { x: 550, y: 360 };
var yearCenters = {
1993: { x: 220, y: 240 },
1994: { x: 330, y: 260 },
1995: { x: 440, y: 280 },
1996: { x: 530, y: 280 },
1997: { x: 620, y: 270 },
1998: { x: 710, y: 270 },
1999: { x: 800, y: 240 },
2000: { x: 230, y: 330 },
2001: { x: 330, y: 340 },
2002: { x: 420, y: 350 },
2003: { x: 510, y: 370 },
2004: { x: 600, y: 380 },
2005: { x: 690, y: 365 },
2006: { x: 775, y: 350 },
2007: { x: 250, y: 440 },
2008: { x: 350, y: 440 },
2009: { x: 450, y: 450 },
2010: { x: 550, y: 450 },
2011: { x: 650, y: 450 },
2012: { x: 750, y: 450 },
2013: { x: 260, y: 550 },
2014: { x: 370, y: 540 },
2015: { x: 490, y: 540 },
2016: { x: 600, y: 540 },
2017: { x: 710, y: 540 },
};
var yearsTitle = {
1993: { x: 100, y: 40 },
1994: { x: 250, y: 30 },
1995: { x: 400, y: 30 },
1996: { x: 525, y: 30 },
1997: { x: 655, y: 30 },
1998: { x: 780, y: 30 },
1999: { x: 950, y: 35 },
2000: { x: 80, y: 185 },
2001: { x: 230, y: 170 },
2002: { x: 360, y: 180 },
2003: { x: 490, y: 180 },
2004: { x: 635, y: 160 },
2005: { x: 780, y: 170 },
2006: { x: 955, y: 200 },
2007: { x: 80, y: 320 },
2008: { x: 240, y: 285 },
2009: { x: 400, y: 305 },
2010: { x: 550, y: 320 },
2011: { x: 725, y: 300 },
2012: { x: 945, y: 340 },
2013: { x: 90, y: 510 },
2014: { x: 280, y: 525 },
2015: { x: 475, y: 530 },
2016: { x: 625, y: 555 },
2017: { x: 865, y: 590 },
};
var stadiumCenters = {
1: { x: 275, y: 40 },
2: { x: 365, y: 40 },
3: { x: 430, y: 40 },
4: { x: 520, y: 40 },
5: { x: 600, y: 40 },
6: { x: 670, y: 40 },
7: { x: 740, y: 40 },
8: { x: 800, y: 40 },
9: { x: 275, y: 300 },
10: { x: 365, y: 300 },
11: { x: 430, y: 300 },
12: { x: 520, y: 300 },
13: { x: 600, y: 300 },
14: { x: 670, y: 300 },
15: { x: 740, y: 300 },
16: { x: 800, y: 300 },
17: { x: 275, y: 450 },
18: { x: 365, y: 450 },
19: { x: 430, y: 450 },
20: { x: 520, y: 450 },
21: { x: 600, y: 450 },
22: { x: 670, y: 450 },
23: { x: 740, y: 450 },
24: { x: 800, y: 450 },
25: { x: 275, y: 550 },
26: { x: 365, y: 550 },
27: { x: 430, y: 550 },
28: { x: 520, y: 550 },
29: { x: 600, y: 550 },
30: { x: 670, y: 550 },
31: { x: 740, y: 550 },
32: { x: 800, y: 550 },
33: { x: 275, y: 640 },
34: { x: 365, y: 640 },
35: { x: 430, y: 640 },
36: { x: 520, y: 640 },
37: { x: 600, y: 640 },
38: { x: 670, y: 640 },
39: { x: 740, y: 640 },
40: { x: 800, y: 640 },
41: { x: 275, y: 710 },
42: { x: 365, y: 710 },
43: { x: 430, y: 710 },
44: { x: 520, y: 710 },
45: { x: 600, y: 710 },
46: { x: 670, y: 710 },
47: { x: 740, y: 710 },
48: { x: 800, y: 710 },
49: { x: 275, y: 780 },
50: { x: 365, y: 780 },
51: { x: 430, y: 780 },
52: { x: 520, y: 780 },
};
var forceStrength = 0.03;
// These will be set in create_nodes and create_vis
var svg = null;
var bubbles = null;
var nodes = [];
var arc = null;
function charge(d) {
return -Math.pow(d.radius, 2.0) * forceStrength;
}
function chargea(d) {
return -Math.pow(d.radius, 1.0) * forceStrength;
}
var simulation = d3.forceSimulation()
.velocityDecay(0.1)
.force('x', d3.forceX().strength(forceStrength).x(center.x))
.force('y', d3.forceY().strength(forceStrength).y(center.y))
.force('charge', d3.forceManyBody().strength(charge))
.on('tick', ticked);
simulation.stop();
var fillColor = d3.scaleOrdinal()
.domain(['Disney','Warner Bros.','Universal','Sony','Paramount','Fox','Lionsgate','Independent']
) .range(['FireBrick','SteelBlue','Orange','Cyan ','YellowGreen ','PowderBlue','Plum','Yellow']);
function createNodes(rawData) {
var maxAmount = d3.max(rawData, function (d) { return +d.Balls; });
var radiusScale = d3.scalePow()
.exponent(0.7)
.range([1, 25])
.domain([0, maxAmount]);
var myNodes = rawData.map(function (d) {
return {
id: d.Date,
name: d.Stadium,
radius: radiusScale(+d.Balls),
value: +d.Balls,
year: d.Year,
org: d.Home_Team,
studioid: d.ID,
group: d.Cat,
open: d.Open,
close: d.Close,
score: d.RT,
ow: d.Op,
stadiumid: d.id3,
attendance: d.Attendance,
bp: d.BP,
pct: ((d.Op/d.BO)*100).toFixed(1),
x: Math.random() * 900,
y: Math.random() * 800
};
});
myNodes.sort(function (a, b) { return b.value - a.value; });
return myNodes;
}
var chart = function chart(selector, rawData) {
nodes = createNodes(rawData);
svg = d3.select(selector)
.append('svg')
.attr('width', width)
.attr('height', height)
.attr('class','centerme');
svg.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(400,400)");
bubbles = svg.selectAll('.bubble')
.data(nodes, function (d) { return d.id; });
var arc = d3.arc()
.outerRadius(0)
.innerRadius(0)
.startAngle(0)
.endAngle(0);
var bubblesE = bubbles.enter().append('circle')
.classed('bubble', true)
.attr('r', 0)
.attr('fill', 'Ivory')
.attr('stroke','IndianRed')
/* .attr('fill', function (d) { return fillColor(d.group); })
.attr('stroke', function (d) { return d3.rgb(fillColor(d.group)).darker(); }) */
.attr('stroke-width', 1)
.on('mouseover', showDetail)
.on('mouseout', hideDetail);
bubbles = bubbles.merge(bubblesE);
bubbles.transition()
.duration(1200)
.attr('r', function (d) { return d.radius; })
/* .attr('d.radius',arc)
.attr('outerRadius', function (d) { return d.radius; })
.attr('startAngle', function (d) { (Math.random(360) * 100); })
.attr('endAngle', function (d) { (2 * Math.PI); }) */
simulation.nodes(nodes);
groupBubbles();
};
function ticked() {
bubbles
.attr('cx', function (d) { return d.x; })
.attr('cy', function (d) { return d.y; });
}
function nodeYearPosX(d) {
return yearCenters[d.year].x;
}
function nodeYearPosY(d) {
return yearCenters[d.year].y;
}
function nodeStadiumPosX(d) {
return stadiumCenters[d.stadiumid].x;
}
function nodeStadiumPosY(d) {
return stadiumCenters[d.stadiumid].y;
}
function groupBubbles() {
hideYearTitles();
simulation.force('x', d3.forceX().strength(forceStrength).x(center.x))
.force('y', d3.forceY().strength(forceStrength).y(center.y));
simulation.alpha(1).restart();
}
function splitBubblesa() {
showYearTitles();
simulation.velocityDecay(0.1).force('x', d3.forceX().strength(forceStrength).x(nodeYearPosX))
.force('y', d3.forceY().strength(forceStrength).y(nodeYearPosY))
.force('charge', d3.forceManyBody().strength(charge))
.on('tick', ticked);
simulation.alpha(1).restart();
}
function splitBubblesb() {
hideYearTitles();
simulation.force('x', d3.forceX().strength(forceStrength).x(nodeStudioPos));
simulation.alpha(1).restart();
}
function hideYearTitles() {
svg.selectAll('.year').remove();
}
function showYearTitles() {
var yearsData = d3.keys(yearsTitle);
var years = svg.selectAll('.year')
.data(yearsData);
years.enter().append('text')
.attr('class', 'year')
.attr('x', function (d) { return yearsTitle[d].x; })
.attr('y', function (d) { return yearsTitle[d].y; })
.attr('text-anchor', 'middle')
.style('font-family', 'Lato')
.style('font-size','0px')
.style('fill','Crimson')
.style('font-weight','bolder')
.transition()
.duration(3000)
.style('font-size','12px')
.style('fill','Crimson')
.style('font-weight','bolder')
.text(function (d) { return d; });
}
function showDetail(d) {
d3.select(this).attr('stroke-width',2);
var content = '<span class="title">' + d.value + ' balls at ' + d.name +
'</span><br/>' + '<span class="gameday">' + d.id + ', ' + d.year +
'</span><br/>' + '<span class="name">BP: </span><span class="value">' + d.bp+
'</span>' + '<br/><span class="name">Attendance </span><span class="value">' + d.attendance +
'</span><br/>' +
'<span class="name">Home Team: </span><span class="value">' +
d.org +
'</span>';
tooltip.showTooltip(content, d3.event);
}
function hideDetail(d) {
d3.select(this)
.attr('stroke-width',1);
tooltip.hideTooltip();
}
chart.toggleDisplay = function (displayName) {
if (displayName === 'year') {
splitBubblesa();
} else if (displayName === 'stadiumb') {
splitBubblesb();
} else {
groupBubbles();
}
};
// return the chart function from closure.
return chart;
}
/*
* Below is the initialization code as well as some helper functions
* to create a new bubble chart instance, load the data, and display it.
*/
var myBubbleChart = bubbleChart();
/*
* Function called once data is loaded from CSV.
* Calls bubble chart function to display inside #vis div.
*/
function display(error, data) {
if (error) {
console.log(error);
}
myBubbleChart('#vis', data);
}
/*
* Sets up the layout buttons to allow for toggling between view modes.
*/
function setupButtons() {
d3.select('#toolbar')
.selectAll('.button')
.on('click', function () {
// Remove active class from all buttons
d3.selectAll('.button').classed('active', false);
// Find the button just clicked
var button = d3.select(this);
// Set it as the active button
button.classed('active', true);
// Get the id of the button
var buttonId = button.attr('id');
// Toggle the bubble chart based on
// the currently clicked button.
myBubbleChart.toggleDisplay(buttonId);
});
}
function addCommas(nStr) {
nStr += '';
var x = nStr.split('.');
var x1 = x[0];
var x2 = x.length > 1 ? '.' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
return x1 + x2;
}
d3.csv('hampled.csv', display);
setupButtons();
function floatingTooltip(tooltipId, width) {
// Local variable to hold tooltip div for
// manipulation in other functions.
var tt = d3.select('body')
.append('div')
.attr('class', 'tooltip')
.attr('id', tooltipId)
.style('pointer-events', 'none');
// Set a width if it is provided.
if (width) {
tt.style('width', width);
}
// Initially it is hidden.
hideTooltip();
/*
* Display tooltip with provided content.
*
* content is expected to be HTML string.
*
* event is d3.event for positioning.
*/
function showTooltip(content, event) {
tt.style('opacity', 0.85)
.html(content);
updatePosition(event);
}
/*
* Hide the tooltip div.
*/
function hideTooltip() {
tt.style('opacity', 0.0);
}
/*
* Figure out where to place the tooltip
* based on d3 mouse event.
*/
function updatePosition(event) {
var xOffset = -5;
var yOffset = 5;
var ttw = tt.style('width');
var tth = tt.style('height');
var wscrY = window.scrollY;
var wscrX = window.scrollX;
var curX = (document.all) ? event.clientX + wscrX : event.pageX;
var curY = (document.all) ? event.clientY + wscrY : event.pageY;
var ttleft = ((curX - wscrX + xOffset * 2 + ttw) > window.innerWidth) ?
curX - ttw - xOffset * 2 : curX + xOffset;
if (ttleft < wscrX + xOffset) {
ttleft = wscrX + xOffset;
}
var tttop = ((curY - wscrY + yOffset * 2 + tth) > window.innerHeight) ?
curY - tth - yOffset * 2 : curY + yOffset;
if (tttop < wscrY + yOffset) {
tttop = curY + yOffset;
}
tt
.style('top', tttop + 'px')
.style('left', ttleft + 'px');
}
return {
showTooltip: showTooltip,
hideTooltip: hideTooltip,
updatePosition: updatePosition
};
}
</script>
</html>
https://d3js.org/d3.v4.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.24.0/d3-legend.min.js