console.clear(); var xmin, xmax; // Set the dimensions of the canvas / graph var margin = {top: 70, right: 60, bottom: 20, left: 60}, width = $('#timeline').width() - margin.right - margin.left, height = 400 - margin.top - margin.bottom; // Parse the date / time var parseDate = d3.time.format('%Y-%m-%dT%H:%M:%S.000Z').parse, bisectDate = d3.bisector(function(d) { return d.date; }).left, formatDate = d3.time.format.multi([ ['.%L', function(d) { return d.getMilliseconds(); }], [':%S', function(d) { return d.getSeconds(); }], ['%I:%M', function(d) { return d.getMinutes(); }], ['%I %p', function(d) { return d.getHours(); }], ['%a %d', function(d) { return d.getDay() && d.getDate() != 1; }], ['%b %d', function(d) { return d.getDate() != 1; }], ['%b', function(d) { return d.getMonth(); }], ['%Y', function() { return true; }] ]); // Set the ranges var x = d3.time.scale().range([0, width]); var y = d3.scale.linear().range([height-60, 0]); // Define the axes var xAxis = d3.svg.axis() .scale(x) .orient('bottom') .tickFormat(formatDate) .tickSize(-height, 0); var yAxis = d3.svg.axis() .scale(y) .orient('left') .tickValues([0, 2, 4, 6, 8, 10]) .tickSize(width) .tickPadding(25); // Define Line/Area var interpolation = 'linear'; var area = d3.svg.area() .interpolate(interpolation) .x(function(d) { return x(d.date); }) .y0(y(0)) .y1(function(d) { return y(d.value); }); var line = d3.svg.line() .interpolate(interpolation) .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.value); }); // Create/Define SVG Canvas var svg = d3.select('#timeline').append('svg') .attr('width', '100%') .attr('height', height + margin.top + margin.bottom) .append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); // Get the data d3.json("https://dl.dropboxusercontent.com/u/4971319/timeline.json", function(error, theData) { data = theData.points; data.forEach(function(d) { d.date = new Date(d.ts); d.value = +d.score; }); // HUD Elements var hudLeft = d3.select('#timeline svg').append('rect') .attr('x', 0).attr('y', 0) .attr('width', 60).attr('height', '120%') .attr('class', 'hudLeft'); var hudRight = d3.select('#timeline svg').append('rect') .attr('x', '100%').attr('y', '0') .attr('width', 60).attr('height', '120%') .attr('class', 'hudRight'); // Main SVG selector svg = d3.select('#timeline svg').append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); // Setup Zoom var zoom = d3.behavior.zoom() .scaleExtent([1, 25]) .on('zoom', draw); // Create Gradient Deffinition var gradient = svg.append('defs').append('linearGradient') .attr('id', 'gradient') .attr('x2', '0%') .attr('y2', '100%'); gradient.append('stop') .attr('offset', '0%') .attr('stop-color', '#000') .attr('stop-opacity', 0.12); gradient.append('stop') .attr('offset', '100%') .attr('stop-color', '#000') .attr('stop-opacity', 0); // Create Clipping Mask svg.append('clipPath') .attr('id', 'clip') .append('rect') .attr('x', x(0)) .attr('y', y(1) - 20) .attr('width', x(1) - x(0)) .attr('height', y(0) - y(1) + 20); // Create Axises svg.append('g') .attr('class', 'y axis') .attr('transform', 'translate(' + width + ',0)'); svg.append('g') .attr('class', 'x axis') .attr('transform', 'translate(0,' + (height - margin.bottom) + ')') .attr('clip-path', 'url(#clip)'); // Setup Lines/Areas svg.append('path') .attr('class', 'area') .attr('clip-path', 'url(#clip)') .style('fill', 'url(#gradient)'); svg.append('path') .attr('class', 'extrude') .attr('clip-path', 'url(#clip)') .attr('filter', 'url(#shadow)'); svg.append('path') .attr('class', 'line') .attr('stroke-linejoin', 'round') .attr('clip-path', 'url(#clip)'); // Setup Floating Bubble var focus = svg.append('g') .attr('class', 'focus'); focus.append('foreignObject') .attr('width', '200px') .attr('height', '200px') .attr('x', '-100px') .attr('y', '-200px') .append('xhtml:body') .append('div') .attr('class', 'bubble-wrap') .append('div') .attr('class', 'bubble') .append('div') .attr('class', 'bubble-text'); // Setup Zoom/Hover Pane svg.append('rect') .attr('class', 'pane') .attr('width', width) .attr('height', height) .call(zoom); // Scale the range of the data x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, 10]); zoom.x(x); // Draw the lines/extras svg.select('.area').attr('d', area(data)); svg.select('.extrude').attr('d', line(data)); svg.select('.line').attr('d', line(data)); /*draw();*/ // Draw Axises svg.select('g.x.axis').call(xAxis); svg.select('g.y.axis').call(yAxis); // Setup Bubble d3.select('.pane') .on('mouseover', function() { focus.style('opacity', 1); }) .on('mouseout', function() { focus.style('opacity', 0); }) .on('mousemove', mousemove); // Setup Resizing d3.select(window).on('resize', resize); function mousemove() { var x0 = x.invert(d3.mouse(this)[0]), i = bisectDate(data, x0, 1), d0 = data[i - 1], d1 = data[i], d = x0 - d0.date > d1.date - x0 ? d1 : d0; focus .transition() .duration(50) .attr('transform', 'translate(' + x(d.date) + ', ' + y(d.value) + ')'); focus.select('.bubble-text').html(d.value); } function draw() { limitArea(); svg.select('.x.axis').call(xAxis); svg.select('.y.axis').call(yAxis); svg.select('.extrude').attr('d', line(data)); svg.select('.area').attr('d', area(data)); svg.select('.line').attr('d', line(data)); resize(); } function resize() { width = $('#timeline').width() - margin.right - margin.left; d3.select('#clip rect').attr('width', width); d3.select('#timeline .pane').attr('width', width); x.range([0, width]); xAxis = d3.svg.axis() .scale(x) .orient('bottom') .tickFormat(formatDate) .tickSize(-height, 0); yAxis = d3.svg.axis() .scale(y) .orient('left') .tickValues([0, 2, 4, 6, 8, 10]) .tickSize(width) .tickPadding(25); d3.select('g.x.axis').call(xAxis); d3.select('g.y.axis').call(yAxis).attr('transform', 'translate(' + width + ',0)'); limitArea(); // redraw line svg.select('path.extrude').attr('d', line(data)); svg.select('path.area').attr('d', area(data)); svg.select('path.line').attr('d', line(data)); } function limitArea(){ if (x.domain()[0] < parseDate(xmin)) { zoom.translate([0, 0]); } else if (x.domain()[1] > parseDate(xmax)) { zoom.translate([width - width * zoom.scale(),0]); } } }); // ** Update data section (Called from the onclick) function updateData(dataUrl) { // Get the data again d3.json(dataUrl, function(error, theData) { data = theData.points; data.forEach(function(d) { d.date = new Date(d.ts); d.value = +d.score; }); // Scale the range of the data again x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, 10]); // Select the section we want to apply our changes to var svg = d3.select("body").transition(); // Make the changes svg.select(".line") // change the line .duration(750) .attr("d", line(data)); svg.select(".extrude") // change the extrude .duration(750) .attr("d", line(data)); svg.select(".area") // change the extrude .duration(750) .attr("d", area(data)); svg.select(".x.axis") // change the x axis .duration(750) .call(xAxis); svg.select(".y.axis") // change the y axis .duration(750) .call(yAxis); }); }