public code

master
Matt Huntington 9 years ago
parent 1507718e65
commit 063d653acc

@ -1,15 +1,15 @@
svg { svg {
margin:40px; margin:40px; /* give it some margin to show off the axes */
overflow:visible; overflow:visible; /* axes will extend beyond the svg cropping area. Show them anyway */
} }
svg circle { svg circle {
r:5; r:5; /* circles will have a default radius of 5 px */
transition: r 0.5s ease-in-out, fill 0.5s linear; transition: r 0.5s ease-in-out, fill 0.5s linear; /* create transitions for radius and fill */
transform-origin: 50% 50%; transform-origin: 50% 50%; /* when transforming, do it around the center of the object */
} }
circle:hover { circle:hover {
fill:blue; fill:blue; /* when hovered, make the blue */
r:15; r:15; /* make it larger when hovered */
} }

@ -1,22 +1,22 @@
// Initialize app // Initialize app
var WIDTH = 800; var WIDTH = 800; //width of svg
var HEIGHT = 600; var HEIGHT = 600; //height of svg
var MAX_DISTANCE = 5; var MAX_DISTANCE = 5; //max distance that can be run
var MIN_DISTANCE = 0; var MIN_DISTANCE = 0; //min distance that can be run
var MIN_DATE = new Date('2016-1-1'); var MIN_DATE = new Date('2016-1-1'); //min date of entry
var MAX_DATE = new Date('2017-1-1'); var MAX_DATE = new Date('2017-1-1'); //max date of entry
d3.select('svg').style('height', HEIGHT).style('width', WIDTH); d3.select('svg').style('height', HEIGHT).style('width', WIDTH); //set height/width of svg. Could do this in CSS, but values are already here for other reasons
// Create scales // Create scales
var yScale = d3.scaleLinear(); var yScale = d3.scaleLinear(); //create a linear scale
yScale.range([HEIGHT, 0]); //max height matches min data value, since max height is at the bottom yScale.range([HEIGHT, 0]); //max height matches min data value, since max height is at the bottom
yScale.domain([MIN_DISTANCE, MAX_DISTANCE]); yScale.domain([MIN_DISTANCE, MAX_DISTANCE]); //min distance matches min data value, same for max values
var xScale = d3.scaleTime(); var xScale = d3.scaleTime(); //set up scale for x coords and dates
xScale.range([0, WIDTH]); xScale.range([0, WIDTH]);
xScale.domain([MIN_DATE, MAX_DATE]); xScale.domain([MIN_DATE, MAX_DATE]);
@ -24,34 +24,35 @@ xScale.domain([MIN_DATE, MAX_DATE]);
// SVG click handler // SVG click handler
d3.select('svg').on('click', function(d){ d3.select('svg').on('click', function(d){
var x = d3.event.offsetX; //d3.event contains data for click event
var x = d3.event.offsetX; //use offset to get point within svg container
var y = d3.event.offsetY; var y = d3.event.offsetY;
var distance = yScale.invert(y); var distance = yScale.invert(y); //get distance from click point in svg
var date = xScale.invert(x); var date = xScale.invert(x); //get date from click point in svg
logRun({ logRun({ //log a run with data
date: date, date: date,
distance: distance distance: distance
}, render); }, render); //call the render callback
}); });
// Log a new run in the server // Log a new run in the server
var logRun = function(runObject, callback){ var logRun = function(runObject, callback){
d3.request('/runs') d3.request('/runs') //make a request to the server
.header("Content-Type", "application/json") .header("Content-Type", "application/json") //tell the server we're sending JSON data
.post( .post(
//must turn data object into string //must turn data object into string
JSON.stringify(runObject), JSON.stringify(runObject),
callback callback //call whatever callback is passed
); );
}; };
//Render circles //Render circles
//var dateParser = d3.time.format("%Y-%m-%d %H:%M:%S.%L +00:00"); //var dateParser = d3.time.format("%Y-%m-%d %H:%M:%S.%L +00:00"); -- can use this if date format is not proper
var render = function(){ var render = function(){
d3.json('/runs', function(error, data){ d3.json('/runs', function(error, data){ //get all run data
//bind circles with data //bind circles with data
var circles = d3.select('svg').selectAll('circle')//ghost selction of circles var circles = d3.select('svg').selectAll('circle')//ghost selction of circles
.data(data, function(d){ .data(data, function(d){
@ -60,38 +61,38 @@ var render = function(){
}); });
//add extra circles if there is extra data //add extra circles if there is extra data
circles circles
.enter() .enter() //for all data that has not yet been mapped...
.append('circle') .append('circle') //create a circle
.attr('cx', function(datum, index){ .attr('cx', function(datum, index){
//convert date value into pixel value //convert date value into pixel value and set it to cx
return xScale(new Date(datum.date)); return xScale(new Date(datum.date));
}) })
.attr('cy', function(datum, index){ .attr('cy', function(datum, index){
//convert distance datum into pixel value //convert distance datum into pixel value and set it to cy
return yScale(datum.distance); return yScale(datum.distance);
}); });
//if any circles remain that don't match data, remove them //if any circles remain that don't match data, remove them
circles.exit().remove(); circles.exit().remove();
//attach event handlers after circle creation //attach event handlers after circle creation
attachDragHandlers(); attachDragHandlers(); //attach drag handlers
attachDeleteHandlers(); attachDeleteHandlers(); //attach delete handlers
}); });
}; };
render(); render(); //render on page load
//Attach click handler to circles //Attach click handler to circles
var attachDeleteHandlers = function(){ var attachDeleteHandlers = function(){
d3.selectAll('circle').on('click', function(d){ d3.selectAll('circle').on('click', function(d){
//prevent svg click for run creation //prevent svg click for run creation
d3.event.stopPropagation(); d3.event.stopPropagation();
//if not dragging (event's default is not prevented) //if not dragging (event's default is not prevented)
if(!d3.event.defaultPrevented){ if(!d3.event.defaultPrevented){
//mkae API call //mkae API call
d3.request('/runs/'+d.id) d3.request('/runs/'+d.id)
.header("Content-Type", "application/json") .header("Content-Type", "application/json") //we're sending data
.send('DELETE', render); .send('DELETE', render); //send a DELETE request
} }
}); });
}; };
@ -100,31 +101,32 @@ var attachDeleteHandlers = function(){
var attachDragHandlers = function(){ var attachDragHandlers = function(){
//create drag behavior //create drag behavior
var drag = d3.drag() var drag = d3.drag()
.on('end', function(d){ .on('end', function(d){ //when dragging has finished
//prevent default for deleting run handler //prevent default for deleting run handler
d3.event.sourceEvent.preventDefault(); d3.event.sourceEvent.preventDefault();
//set new date and distance on datum object //set new date and distance on datum object
var date = xScale.invert(d3.event.x); var date = xScale.invert(d3.event.x); //d3.event.x is used for drag behavior... different from normal click event
var distance = yScale.invert(d3.event.y); var distance = yScale.invert(d3.event.y);
d.date = date; d.date = date; //update data on element
d.distance = distance; d.distance = distance;
//make api call //make api call to update DB
d3.request('/runs/'+d.id) d3.request('/runs/'+d.id)
.header("Content-Type","application/json") .header("Content-Type","application/json") //we're sending JSON
.send('PUT', JSON.stringify(d), render);//pass alterted 'd' object to API .send('PUT', JSON.stringify(d), render);//pass alterted 'd' object to API
}) })
.on('drag', function(d){ .on('drag', function(d){ //while dragging...
//change position of cirlce while draging //change position of cirlce
var x = d3.event.x; var x = d3.event.x; //get new position from d3.event.x
var y = d3.event.y; var y = d3.event.y; //get new position from d3.event.y
d3.select(this).attr('cx',x); d3.select(this).attr('cx',x); //update element visually
d3.select(this).attr('cy',y); d3.select(this).attr('cy',y);
}); });
//attach drag behavior to circle elements //attach drag behavior to circle elements
d3.selectAll('circle').call(drag); d3.selectAll('circle').call(drag);
}; };
d3.select('svg').append('g').attr('transform', 'translate(0,' + HEIGHT + ')').call(d3.axisBottom(xScale)); //create axes
d3.select('svg').append('g').call(d3.axisLeft(yScale)); d3.select('svg').append('g').attr('transform', 'translate(0,' + HEIGHT + ')').call(d3.axisBottom(xScale)); //x axes must be moved to bottom of svg
d3.select('svg').append('g').call(d3.axisLeft(yScale)); //y axis is good as it is

Loading…
Cancel
Save