You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
4.4 KiB

const WIDTH = 800;
const HEIGHT = 600;
let runs = [
{
id: 1,
date: 'October 1, 2017 at 4:00PM',
distance: 5.2
},
{
id: 2,
date: 'October 2, 2017 at 5:00PM',
distance: 7.0725
},
{
id: 3,
date: 'October 3, 2017 at 6:00PM',
distance: 8.7
}
];
d3.select('svg')
.style('width', WIDTH)
.style('height', HEIGHT);
const parseTime = d3.timeParse("%B%e, %Y at %-I:%M%p");
const formatTime = d3.timeFormat("%B%e, %Y at %-I:%M%p");
const xScale = d3.scaleTime();
xScale.range([0,WIDTH]);
const xDomain = d3.extent(runs, (datum, index) => {
return parseTime(datum.date);
});
xScale.domain(xDomain);
const yScale = d3.scaleLinear(); //create the scale
yScale.range([HEIGHT, 0]); //set the visual range (e.g. 600 to 0)
const yDomain = d3.extent(runs, (datum, index) => {
return datum.distance; //compare distance properties of each item in the data array
})
yScale.domain(yDomain);
const render = () => {
//adjust the code at the top of your render function
d3.select('#points').html(''); //clear out all circles when rendering
d3.select('#points').selectAll('circle') //add circles to #points group, not svg
.data(runs)
.enter()
.append('circle');
d3.selectAll('circle').data(runs)
.attr('cy', (datum, index) => {
return yScale(datum.distance);
});
d3.selectAll('circle')
.attr('cx', (datum, index) => {
return xScale(parseTime(datum.date)); //use parseTime to convert the date string property on the datum object to a Date object, which xScale then converts to a visual value
});
//put this at the bottom of the render function, so that click handlers are attached when the circle is created
d3.selectAll('circle').on('click', (event, datum) => {
event.stopPropagation(); //stop click event from propagating to the SVG element and creating a run
runs = runs.filter((run, index) => { //create a new array that has removed the run with the correct id. Set it to the runs var
return run.id != datum.id;
});
render(); //re-render dots
createTable(); //re-render table
});
const drag = function(event, datum) {
const x = event.x;
const y = event.y;
d3.select(this).attr('cx', x);
d3.select(this).attr('cy', y);
}
const dragEnd = (event, datum) => {
const x = event.x;
const y = event.y;
const date = xScale.invert(x);
const distance = yScale.invert(y);
datum.date = formatTime(date);
datum.distance = distance;
createTable();
}
const dragBehavior = d3.drag()
.on('drag', drag)
.on('end', dragEnd);
d3.selectAll('circle').call(dragBehavior);
}
render();
const bottomAxis = d3.axisBottom(xScale); //pass the appropriate scale in as a parameter
d3.select('svg')
.append('g') //put everything inside a group
.attr('id', 'x-axis') //add an id
.call(bottomAxis) //generate the axis within the group
.attr('transform', 'translate(0,'+HEIGHT+')'); //move it to the bottom
const leftAxis = d3.axisLeft(yScale);
d3.select('svg')
.append('g')
.attr('id', 'y-axis') //add an id
.call(leftAxis); //no need to transform, since it's placed correctly initially
const createTable = () => {
d3.select('tbody').html(''); //clear out all rows from the table
for (let i = 0; i < runs.length; i++) {
const row = d3.select('tbody').append('tr');
row.append('td').html(runs[i].id);
row.append('td').html(runs[i].date);
row.append('td').html(runs[i].distance);
}
}
createTable();
d3.select('svg').on('click', (event) => {
const x = event.offsetX; //gets the x position of the mouse relative to the svg element
const y = event.offsetY; //gets the y position of the mouse relative to the svg element
const date = xScale.invert(x) //get a date value from the visual point that we clicked on
const distance = yScale.invert(y); //get a numeric distance value from the visual point that we clicked on
const newRun = { //create a new "run" object
id: ( runs.length > 0 ) ? runs[runs.length-1].id+1 : 1, //add this line
date: formatTime(date), //format the date object created above to a string
distance: distance //add the distance
}
runs.push(newRun); //push the new run onto the runs array
createTable(); //render the table
render(); //add this line
});
const zoomCallback = (event) => {
d3.select('#points').attr("transform", event.transform);
d3.select('#x-axis')
.call(bottomAxis.scale(event.transform.rescaleX(xScale)));
d3.select('#y-axis')
.call(leftAxis.scale(event.transform.rescaleY(yScale)));
}
const zoom = d3.zoom()
.on('zoom', zoomCallback);
d3.select('svg').call(zoom);