xxxxxxxxxx
<html lang='en-GB'>
<head></head>
<style>
body {
font-family: sans-serif;
font-size: 16px;
line-height: 1.4;
display: block;
margin: 0;
padding: 0;
color: #333;
-webkit-font-smoothing: antialiased;
}
a, a:link, a:visited {
text-decoration: none;
color: #012157;
}
a:hover {
text-decoration: underline;
}
h3 {
font-family: sans-serif;
font-size: 26px;
font-weight: 900;
line-height: 30px;
margin: 0 0 5px 0;
}
p {
font-family: sans-serif;
font-size: 15px;
margin: 0;
}
/*template styles*/
.gia-chart-wrapper {
max-width: 960px;
margin: 0 auto;
}
.gia-chart {
position: relative;
}
.gia-source {
margin-top: 5px;
color: #bdbdbd;
padding: 3px 0;
font-size: 12px;
font-family: sans-serif;
}
/*chart styles*/
.y.axis line {
fill: none;
stroke: #dcdcdc;
stroke-dasharray: 1px 1px;
shape-rendering: crispEdges;
stroke-width: 1px;
}
.x.axis line {
fill: none;
stroke: #333333;
shape-rendering: crispEdges;
stroke-width: 1px;
}
.tick.g-baseline line {
stroke: #333333;
stroke-dasharray: 0;
stroke-width: 1px;
}
.tick.g-baseline text {
display: none;
}
.axis text {
font-family: sans-serif;
font-size: 12px;
pointer-events: none;
fill: #767676;
}
.y.axis text {
text-anchor: start !important;
font-size:12px;
fill: #767676;
}
.domain {
display: none;
}
.g-label-text {
font-family: sans-serif;
font-size: 13px;
text-shadow: 2px 0 0 #fff, -2px 0 0 #fff, 0 2px 0 #fff, 0 -2px 0 #fff, 1px 1px #fff, -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff;
opacity: 0;
}
.g-label-circle {
fill: #012157;
}
.cat-path {
stroke: #dcdcdc;
stroke-width: 1px;
fill: none;
opacity: 0.8;
}
.path-select {
stroke: #e6012a;
stroke-width: 2;
}
.path-highlight {
stroke: #012157;
stroke-width: 2px;
fill: none;
opacity: 1;
}
.tooltip {
position: absolute;
text-align: left;
padding: 2px;
font-family: sans-serif;
font-size: 13px;
line-height: 16px;
text-shadow: 2px 0 0 #fff, -2px 0 0 #fff, 0 2px 0 #fff, 0 -2px 0 #fff, 1px 1px #fff, -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff;
pointer-events: none;
color: #333333;
opacity: 0;
background-color: #ffffff;
border: 1px solid #dcdcdc;
}
.visible {
transition: opacity 0.3s;
opacity: 1;
}
.logo {
width: 50px;
float: left;
margin-right: 10px;
}
.headline {
width: 100%;
}
.hed-text {
float: left;
width: calc(100% - 60px);
}
.intro {
margin-top:5px;
}
.voronoi path {
fill: none;
pointer-events: all;
}
@media(max-width:400px) {
h3 {
font-size: 22px;
line-height: 24px;
}
.logo {
width: 40px;
}
.hed-text {
width: calc(100% - 50px);
}
}
</style>
<body>
<main>
<div class='gia-chart-wrapper'>
<div class="headline">
<img class="logo" src="huskies.png">
<div class="hed-text">
<h3>UConn women's basketball scoring leaders</h3>
</div>
<p>Eighteen players scored during the 100-game win streak between Nov. 23, 2014 and Feb. 13, 2017 </p>
</div>
<div class='gia-chart'></div>
<div class='gia-source'>
Source: <a href="https://www.uconnhuskies.com/sports/w-baskbl/conn-w-baskbl-body.html">UConn Women's Basketball</a>
</div>
</div>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script>
<script>
//Margin conventions
var margin = {top: 15, right: 70, bottom: 30, left: 0};
var widther = d3.select(".gia-chart").node().clientWidth;
var width = widther - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//Appends the svg to the chart-container div
var svg = d3.select(".gia-chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var div = d3.select(".gia-chart").append("div")
.attr("class", "tooltip");
//Creates the xScale
var xScale = d3.scale.linear()
.range([0, width]);
//Creates the yScale
var yScale = d3.scale.linear()
.range([height, 0]);
//Defines the y axis styles
var yAxis = d3.svg.axis()
.scale(yScale)
.tickSize(-width)
.ticks(8)
.orient("left");
//Defines the y axis styles
var xAxis = d3.svg.axis()
.scale(xScale)
.tickPadding(8)
.orient("bottom")
.ticks(10);
//line function convention (feeds an array)
var line = d3.svg.line()
.defined(function(d) { return d.cumm_points != 0; })
.x(function(d) { return xScale(d.win_number); })
.y(function(d) { return yScale(d.cumm_points); });
//Loads the data
d3.csv("uconn_scorers.csv", ready);
function ready(err, data) {
if (err) throw "error loading data";
//FORMAT data
data.forEach(function(d) {
d.cumm_points = +d.cumm_points;
d.points = +d.points;
d.win_number = +d.win_number;
});
//Organizes the data
data.sort(function(a,b) { return a.win_number - b.win_number; });
var maxY = d3.max(data, function(d) { return d.cumm_points; });
var maxX = d3.max(data, function(d) { return d.win_number; });
//Nest the data
var dataByCategory = d3.nest()
.key(function(d) { return d.player;})
.entries(data);
console.log(dataByCategory);
//Defines the xScale max
xScale.domain(d3.extent(data, function(d) { return d.win_number; }));
//Defines the yScale max
yScale.domain([0, maxY]);
//Appends the y axis
var yAxisGroup = svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("g")
.classed("g-baseline", function(d) {return d == 0});
d3.selectAll(".y.axis text")
.attr("transform", "translate(3, -10)");
//Appends the x axis
var xAxisGroup = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
//Moves selction to front
d3.selection.prototype.moveToFront = function() {
return this.each(function(){
this.parentNode.appendChild(this);
});
};
//Moves selction to back
d3.selection.prototype.moveToBack = function() {
return this.each(function() {
var firstChild = this.parentNode.firstChild;
if (firstChild) {
this.parentNode.insertBefore(this, firstChild);
}
});
};
var playersOfInterest = {
'Breanna Stewart': true,
'Kaleena Mosqueda-Lewis': true,
'Katie Lou Samuelson': true
//'Kia Nurse': true
};
//data join for paths
var drawline = svg.selectAll(".cat-path")
.data(dataByCategory)
.enter()
.append("path")
.attr("class", "cat-path")
.attr('data-player', d => d.key)
.attr("d", function(d) { return line(d.values) })
.classed("path-highlight", function(d) {return d.key in playersOfInterest});
//Appending the voronoi
var voronoiGroup = svg.append("g")
.attr("class", "voronoi");
var voronoi = d3.geom.voronoi()
.x( d => xScale( d.win_number ) )
.y( d => yScale( d.cumm_points ) )
.clipExtent([[-margin.left, -margin.top], [width + margin.right, height + margin.bottom]]);
var layout = voronoi( data.filter( d => d.cumm_points ) );
voronoiGroup.selectAll(".cat-path")
.data( layout.filter( Boolean ) ) // remove empty elements in array
.enter().append("path")
.attr("d", function(d) {
return "M" + d.join("L") + "Z";
})
.datum(function(d) { return d.point; })
.on({
"mousemove": mousemove
});
var selectedPlayer = null;
//Add mousemove events
function mousemove(d) {
//Lock toolitp to line
div
.html( d.player + '<br>' + d.cumm_points )
.style( 'left', xScale( d.win_number ) + 'px' )
.style( 'top', yScale( d.cumm_points ) + 'px' );
//Hover on player line becomes selectedPlayer
if ( d.player === selectedPlayer ) return;
//Deselected existing player
if ( selectedPlayer ) {
d3.select( '[data-player="' + selectedPlayer + '"]' ).classed( 'path-select', false );
}
//Move selected player to front
d3.select( '[data-player="' + d.player + '"]' )
.classed( 'path-select', true )
.moveToFront();
selectedPlayer = d.player;
//Behavoir for already highlighed players
if ( d.player in playersOfInterest ) {
//Show tooltip
div.classed( 'visible', true );
//Hide original label text
d3.selectAll( '.g-label-text' ).classed( 'visible', false );
} else {
//Show tooltip
div.classed( 'visible', true );
//Hide original label text
d3.selectAll( '.g-label-text' ).classed( 'visible', false );
}
}
var labelPoints = dataByCategory
.filter( series => series.key in playersOfInterest )
.map( series => {
var i = series.values.length;
while ( i-- ) {
var point = series.values[i];
if ( point.cumm_points ) {
var split = series.key.lastIndexOf( ' ' );
return {
win_number: point.win_number,
cumm_points: point.cumm_points,
firstName: series.key.slice( 0, split ),
lastName: series.key.slice( split + 1 )
};
}
}
});
labels = d3.select( 'svg g' ).selectAll( '.fiddly-bits' )
.data( labelPoints )
.enter().append( 'g' ).attr( 'class', 'fiddly-bits' );
console.log( labels );
// circles
labels.append( 'circle' )
.attr( 'r', 2 )
.attr("class", "g-label-circle");
// labels
labels.append( 'text' )
.attr("x", 5)
.attr("y", 5)
.text(d => d.firstName)
.attr("class", "g-label-text visible");
labels.append( 'text' )
.attr("x", 5)
.attr("y", 20)
.text(d => d.lastName)
.attr("class", "g-label-text visible");
resized();
// we have the final point
// var labelElements = svg.append("g")
// .attr("class", "fiddly-bits")
// var label = labelElements
// .append("g")
// .attr("class", "g-label-element")
// .attr("transform", "translate(" + xScale(point.win_number) + "," + yScale(point.cumm_points) + ")" );
// label.append("circle")
// .attr("r", 2)
// .attr("class", "g-label-circle");
// var split = series.key.lastIndexOf( ' ' );
// var firstName = series.key.slice( 0, split );
// var lastName = series.key.slice( split + 1 );
// label.append("text")
// .attr("x", 5)
// .attr("y", 5)
// .text(firstName)
// .attr("class", "g-label-text visible");
// label.append("text")
// .attr("x", 5)
// .attr("y", 20)
// .text(lastName)
// .attr("class", "g-label-text visible");
// return;
// }
// }
// });
// dataByCategory.forEach( series => {
// if ( !( series.key in playersOfInterest ) ) return;
// var i = series.values.length;
// while ( i-- ) {
// var point = series.values[i];
// if ( point.cumm_points ) {
// console.log( point );
// // we have the final point
// var labelElements = svg.append("g")
// .attr("class", "fiddly-bits")
// var label = labelElements
// .append("g")
// .attr("class", "g-label-element")
// .attr("transform", "translate(" + xScale(point.win_number) + "," + yScale(point.cumm_points) + ")" );
// label.append("circle")
// .attr("r", 2)
// .attr("class", "g-label-circle");
// var split = series.key.lastIndexOf( ' ' );
// var firstName = series.key.slice( 0, split );
// var lastName = series.key.slice( split + 1 );
// label.append("text")
// .attr("x", 5)
// .attr("y", 5)
// .text(firstName)
// .attr("class", "g-label-text visible");
// label.append("text")
// .attr("x", 5)
// .attr("y", 20)
// .text(lastName)
// .attr("class", "g-label-text visible");
// return;
// }
// }
// });
//RESPONSIVENESS
d3.select(window).on("resize", resized);
function resized() {
//new margin
var newMargin = {top: 15, right: 70, bottom: 30, left: 0};
var newWidther = d3.select(".gia-chart").node().clientWidth;
var newWidth = newWidther - newMargin.left - newMargin.right;
//Change the width of the svg
d3.select("svg")
.attr("width", newWidth + newMargin.left + newMargin.right);
//Change the xScale
xScale
.range([0, newWidth]);
//Update the line
line = d3.svg.line()
.defined(function(d) { return d.cumm_points != 0; })
.x(function(d) { return xScale(d.win_number); })
.y(function(d) { return yScale(d.cumm_points); });
d3.selectAll('.cat-path')
.attr("d", function(d) { return line(d.values) })
//Update voronoi
voronoi = d3.geom.voronoi()
.x( d => xScale( d.win_number ) );
//Updates xAxis
d3.selectAll(".x.axis")
.call(xAxis);
//Updates ticks
xAxis
.scale(xScale);
//Updates yAxis
d3.selectAll(".y.axis")
.call(yAxis);
yAxis
.tickSize(-newWidth);
labels
.attr("transform", d => "translate(" + xScale(d.win_number) + "," + yScale(d.cumm_points) + ")" );
};
}
</script>
</body>
</html>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js