D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
alanmclean
Full window
Github gist
Run Pace Viz experiment #10
<html> <body> <link rel="stylesheet" type="text/css" href="normalize.css"> <style type="text/css"> .avgPaceLabel, .avgPaceLabel2, .runTimeLabel, .runTimeLabel2{ font-family:arial,sans-serif; font-size:11px; fill:#666; /*font-weight:bold;*/ } .avgPaceLabel2,.runTimeLabel2{ font-weight:bold; font-size:13px; fill:rgb(60,146,186); } .paceBar{ stroke:rgba(0,0,0,.2); stroke-width:1px; shape-rendering:crispEdges; stroke-dashArray: 2,4; display:none; } .paceLabel{ font-family:arial,sans-serif; font-size:11px; fill:#aaa; } .mileMarkerText{ /*font-weight:bold;*/ font-family:arial,sans-serif; font-size:11px; fill:#777; /*font-weight:bold;*/ } .mileMarker{ stroke:rgba(0,0,0,.3); stroke-width:1px; shape-rendering:crispEdges; stroke-dashArray: 2,4; } .block{ /*fill:#fff;*/ } .positionCircle{ fill:#999; } #container{ width:1000px; margin: 20px auto; position:relative; /*height:500px;*/ } .paceline{ stroke:rgba(0,0,100,0); stroke-width:1.5px; /*fill:none;*/ /*opacity:0;*/ /*stroke:#000;*/ } .avg{ stroke: rgb(163,210,225); opacity:.9; /*stroke-dashArray: 2,4;*/ shape-rendering:crispEdges; /*fill:none;*/ stroke-width:1px; fill:none; } .goal{ stroke:#f9a7a8; opacity:1; /*stroke-dashArray: 2,4;*/ shape-rendering:crispEdges; /*fill:none;*/ stroke-width:1px; fill:none; } .area.above { fill: rgb(163,210,225); fill-opacity:.7; } .area.below { fill: #f9a7a8; fill-opacity:.8; } .elevationGraph{ fill: #ccc; fill-opacity:.3; } .paceline-above{ stroke:#900; } .hed{ font-size:25px; text-align:center; margin-left:-85px; font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; font-weight: 300; } #faster{ font-size: 11px; color: #999; width: 50px; height: 38px; position: absolute; z-index: 100; background: url(arrowbottom.gif) no-repeat 20px top; left: 53px; top: 269px; padding-top: 21px; text-align: center; text-transform:uppercase; display:none; } #slower{ font-size: 11px; color: #999; width: 50px; height: 38px; position: absolute; z-index: 100; background: url(arrowtop.gif) no-repeat 20px bottom; left: 53px; top: 210px; padding-top: 21px; text-align: center; text-transform:uppercase; display:none; } .boxRight{ height:40px; width:4px; border:1px solid #ddd; border-right:none; position:absolute; } .boxLeft{ height:50px; width:10px; border:1px solid #ddd; border-left:none; position:absolute; } .altitude{ fill:#999; fill-opacity:.7; } </style> <div id="container"> <h3 class="hed">Race Pace Analysis</h3> <div id="chart"></div> <div id="faster">Faster</div> <div id="slower">Slower</div> </div> <script src="https://d3js.org/d3.v3.min.js"></script> <script type="text/javascript" src="streams.js"></script> <script> // fastestPaceDataMetersPerSec = d3.max(paceData, function(d){ return d.y }) // slowestPaceDataMetersPerSec = d3.min(paceData, function(d){ return d.y }) var margin = {top: 20, right: 156, bottom: 150, left: 100 }, width = 900 - margin.left - margin.right, height = 400 - margin.top - margin.bottom; var chart = d3.select("#container").append("svg") .attr('xml:space', "preserve") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var x = d3.scale.linear().range([0, width]).domain(d3.extent(desiredStream, function(d){ return d.x })) var y = d3.scale.linear().range([height, 40]).domain(d3.extent(desiredStream, function(d){ return d.y })) var xDistance = d3.scale.linear().range([0, width]).domain(d3.extent(distanceStream, function(d){ return d })) var avg = d3.mean(desiredStream, function(d){ return d.y }); var goal = 480; var altX = d3.scale.linear().range([0, width]).domain(d3.extent(elevationStream, function(d){ return d.x })) var altY = d3.scale.linear().range([height, 70]).domain(d3.extent(elevationStream, function(d){ return d.y })) // chart.append('text') chart.selectAll('.mileMarker') .data([1,2,3,4,5,6,7,8]).enter() .append('line') .attr('class', 'mileMarker') .style('opacity',0) .attr('x1', function(d){ return xDistance(d) }) .attr('x2', function(d){ return xDistance(d) }) .attr('y1', 0) .attr('y2', height) .attr('clip-path', 'url(#clip-above)') .transition() .delay(function(d, i) { return i / 8 * 700; }) .style('opacity',1) chart.selectAll('.mileMarkerText') .data([1,2,3,4,5,6,7,8]).enter() .append('text') .attr('class', 'mileMarkerText') .style('opacity',0) .attr('x', function(d){ return xDistance(d) }) .attr('dx', -3) .attr('dy', -6) .attr('y', 0) .text(function(d){ if(d !== 1){ return d+'mi.' }else{ return d+'mile'} }) .transition() .delay(function(d, i) { return i / 8 * 700; }) .style('opacity',1) var desiredPaces = [8,10,12,14], maxln = d3.max(desiredPaces), paddingDesiredPaces = desiredPaces.map(function(d){ return (" " + d).slice((-2)) }) chart.selectAll('.avgPaceLabel') .data([avg]) .enter() .append('text') .attr('class','avgPaceLabel') .attr('text-anchor','start') .attr('x',width+11) .attr('dy',-16) .attr('y',function(d){ return y(d) }) .text('AVG. PACE') chart.selectAll('.avgPaceLabel2') .data([avg]) .enter() .append('text') .attr('class','avgPaceLabel2') .attr('text-anchor','start') .attr('x',width+11) .attr('y',function(d){ return y(d) }) .attr('dy',1) .text('9:07min/mile') chart.selectAll('.runTimeLabel') .data([goal]) .enter() .append('text') .attr('class','avgPaceLabel') .attr('text-anchor','end') .attr('x',0) .attr('dx',-10) .attr('y',function(d){ return y(d) }) .attr('dy',19) .text('GOAL PACE') chart.selectAll('.runTimeLabel2') .data([goal]) .enter() .append('text') .attr('class','avgPaceLabel2') .attr('text-anchor','end') .attr('x',0) .attr('dx',-10) .attr('y',function(d){ return y(d) }) .attr('dy',5) .text('8:15min/mi') // chart.selectAll('.paceLabel') // .data(desiredPaces).enter() // .append('text') // .attr('class', 'paceLabel') // .style('opacity',0) // .attr('x', 0) // .attr('dx',-10) // .attr('text-anchor','end') // .attr('y', function(d){ return y(d * 60) }) // .text(function(d,i){ return (paddingDesiredPaces[i])+' min/mi' }) // .transition() // .delay(function(d, i) { return i / 8 * 700; }) // .style('opacity',1) chart.selectAll('.paceBar') .data(desiredPaces).enter() .append('line') .attr('class', 'paceBar') // .style('opacity',0) .attr('x1', 0) .attr('x2', width) .attr('y1', function(d){ return y(d * 60) }) .attr('y2', function(d){ return y(d * 60) }) // .text(function(d,i){ return (paddingDesiredPaces[i])+' min/mi' }) // .transition() // .delay(function(d, i) { return i / 8 * 700; }) // .style('opacity',1) // initalT = chart.transition().duration(700); var line = d3.svg.area() .interpolate("basis") .x(function(d) { return x(d.x); }) .y(function(d) { return y(d.y); }); var area = d3.svg.area() .interpolate("basis") .x(function(d) { return x(d.x); }) .y1(function(d) { return y(goal); }); var altLine = d3.svg.area() .interpolate("basis") .x(function(d) { return altX(d.x); }) .y(function(d) { return altY(d.y); }); var altArea = d3.svg.area() .interpolate("basis") .x(function(d) { return altX(d.x); }) .y0(function(d) { return y(goal); }) .y1(function(d) { return altY(d.y); }); // var tempArea = d3.svg.area() // .interpolate("basis") // .x(function(d) { return x(d.x); }) // .y0(function(d) { return y(goal); }); var meanData = desiredStream.map(function(d){ return { x: d.x, y: goal }; }) var startData = desiredStream.map(function(d){ return { x: paceData[0].x, y: goal }; }) // startData[startData.length-1].x = paceData[startData.length-1].x // chart.datum(meanData) var t0 = chart.transition().duration(600) var pline = chart.append("path") .datum(startData) .attr("class", "paceline animateMe") .attr("d", line) .style('opacity',0) var avgLine = d3.select('svg').append("svg:line") .attr("x1", margin.left) .attr("x2", 0) .attr('class', 'avg animateMe') .attr("y1", y(avg)+margin.top + 1) .attr("y2", y(avg)+margin.top + 1) .style('opacity',0) .transition() .duration(600) .style('opacity',1) .attr('x2',width + margin.left) var avgLine = d3.select('svg').append("svg:line") .attr("x1", margin.left) .attr("x2", 0) .attr('class', 'goal animateMe') .attr("y1", y(goal)+margin.top) .attr("y2", y(goal)+margin.top) .style('opacity',0) .transition() .duration(600) .style('opacity',1) .attr('x2',width + margin.left) // var l = pline.node().getTotalLength(); // pline.attr("stroke-dasharray", l + " " + l); // t0.selectAll('.startPaceline').attr("stroke-dashoffset", 0); pline.datum(meanData); var t1 = chart.transition().duration(900).delay(200) t1.selectAll('.paceline').attr('d',line).style('opacity',1) var altGraph = chart.append("path") .datum(elevationStream) .attr("id", "elevationGraph") .attr("class", "elevationGraph") .attr("d", altArea); var c1_clip = chart.append("clipPath") .datum(meanData) .attr("id", "clip-above") .append("path") .attr("d", area.y0(0)); var c1_clip2 = chart.append("path") .datum(meanData) .attr("id", "area-above") .attr("class", "area above") .attr("clip-path", "url(#clip-above)") .attr("d", area.y0(function(d) { return y(d.y); })); var c3 = chart.append("path") .datum(meanData) .attr("class", "area below") .attr("clip-path", "url(#clip-below)") .attr("d", area); var c4 = chart.append("clipPath") .datum(meanData) .attr("id", "clip-below") .append("path") .attr("d", area.y1(height)) pline.datum(desiredStream); c1_clip.datum(desiredStream); c1_clip2.datum(desiredStream); var t2 = t1.transition() t2.selectAll('.paceline').attr('d',line); t2.selectAll('#clip-above').attr("d", area.y0(0)); t2.selectAll('#area-above').attr("d", area.y0(function(d) { return y(d.y); })); c3.datum(desiredStream); c4.datum(desiredStream); t2.selectAll('#clip-below').attr("d", area.y1(height - margin.top - margin.bottom)); t2.selectAll('.area.below').attr("d", area); d3.select('#container').append('div') .attr('class','boxRight') .attr('style','left:'+(margin.left + width + 3)+'px; top: '+(margin.top+y(avg) + 21)+'px;') // pline.datum(paceData) // .transition() // .duration(1000) // .attr('d',line) // pline // .attr("d", line) // chart.datum(paceData) // pline.datum(paceData) // pline.attr('d', line) // d3.select('.area.below').transition().attr('d',area) // d3.select('#clip-below').attr('d',area.y1(height - margin.top - margin.bottom)) // chart.append("path") // .attr("class", "area below") // .attr("clip-path", "url(#clip-below)") // .attr("d", area); // var pline = chart.append('svg:path').attr([0,0]).enter() // var line = "M"+paceData[0].x + " " + paceData[0].y // var avgLine = chart.append("svg:line") // .attr("x1", 0) // .attr("x2", 0) // .attr('class', 'avg animateMe') // .attr("y1", y(avg)) // .attr("y2", y(avg)) // avgLine.attr('x2', width) // pline.attr("stroke-dashoffset", 0); // var gradient = chart.append("svg:defs") // .append("svg:linearGradient") // .attr("id", "gradient") // .attr("x1", "0%") // .attr("y1", "0%") // .attr("x2", "100%") // .attr("y2", "0%") // .attr("spreadMethod", "pad"); // gradient.append("svg:stop") // .attr("offset", "10") // .attr("stop-color", "rgb(255,255,255)") // .attr("stop-opacity", .7); // gradient.append("svg:stop") // .attr("offset", "100%") // .attr("stop-color", "rgba(255,255,255,1)") // .attr("stop-opacity", 1); // var circle = chart.append("circle") // .attr('class','positionCircle') // .attr("r", 5) // .attr("transform", "translate(" + x(paceData[0].x) + "," + y(paceData[0].y) + ")"); // // circle.transition() // // .duration(4000) // // .each("end", function(d,i){ if(d.y !== paceData[paceData.length-1].y) { transition() }) // var block= chart.append('svg:rect').style('fill','url(#gradient)').attr('class','block').attr('x',0).attr('y',0).attr('height',height).attr('width',width); // transition() // // block.transition().ease('linear').duration(4000).attr('x',width) // function transition() { // circle.transition() // .duration(10010) // .ease('linear') // .delay(function(d){ return d }) // .attrTween("transform", translateAlong(pline.node())) // block.transition() // .duration(10000) // .ease('linear') // .attrTween("transform", translateAlong2(pline.node())) // // .each("end", function(d,i){ console.log('d'); }) // } // // Returns an attrTween for translating along the specified path element. // function translateAlong(path) { // var pl = path.getTotalLength(); // return function(d, i, a) { // return function(t) { // if(t >= 0.5) { // circle.remove() // // return "translate(" + p.x + "," + p.y + ")"; // }else{ // var p = path.getPointAtLength(t * pl); // return "translate(" + p.x + "," + p.y + ")"; // } // }; // }; // } // function translateAlong2(path) { // var pl = path.getTotalLength(); // return function(d, i, a) { // return function(t) { // if(t >= 0.5) { // block.remove() // }else{ // var p = path.getPointAtLength(t * pl); // return "translate(" + (p.x) + ",0)"; // } // }; // }; // } // var transitionMe = function(transition){ // if(this.attr('class','avg') === true){ // transition // .duration(4000) // .ease("linear") // .attr('x2', width); // }else{ // transition // .duration(4000) // .ease("linear") // .attr("stroke-dashoffset", 0); // } // }; // d3.selectAll('.animateMe').transition().call(transitionMe); // var pline2 = chart.append("path") // // .attr("class", "paceline-above") // .attr("d", line) // .attr("clip-path", "url(#clip-below)") // fastestPaceDataMetersPerSec = d3.max([fast.y, fastestPaceDataMetersPerSec]) // slowestPaceDataMetersPerSec = d3.min([slow.y, slowestPaceDataMetersPerSec]) </script> </body> </html>
Modified
http://d3js.org/d3.v3.min.js
to a secure url
https://d3js.org/d3.v3.min.js