D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
jadiehm
Full window
Github gist
UConn women's basketball scorers
<!doctype html> <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