|
<!DOCTYPE html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<style> |
|
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } |
|
</style> |
|
</head> |
|
|
|
<body> |
|
<h2>Line Chart One Directional Zoom with Brush</h2> |
|
|
|
<div id="svg-container"></div> |
|
<script> |
|
var data = [ { |
|
date : '01/23/16', units : 60 |
|
}, { |
|
date : '04/01/16', units : 60 |
|
}, { |
|
date : '06/08/16', units : 29 |
|
}, { |
|
date : '07/12/16', units : 81 |
|
}, { |
|
date : '07/29/16', units : 28 |
|
}, { |
|
date : '10/05/16', units : 46 |
|
}, { |
|
date : '01/15/17', units : 87 |
|
}, { |
|
date : '02/01/17', units : 4 |
|
}, { |
|
date : '02/18/17', units : 7 |
|
}, { |
|
date : '03/07/17', units : 80 |
|
}, { |
|
date : '05/31/17', units : 57 |
|
}, { |
|
date : '10/14/17', units : 11 |
|
}, { |
|
date : '11/17/17', units : 94 |
|
}, { |
|
date : '10/14/17', units : 28 |
|
}, { |
|
date : '12/04/17', units : 32 |
|
}, { |
|
date : '12/21/17', units : 44 |
|
} ]; |
|
|
|
var margin = { |
|
top : 20, |
|
right : 20, |
|
bottom : 30, |
|
left : 50 |
|
}, width = 960 - margin.left - margin.right, height = 500 - margin.top |
|
- margin.bottom; |
|
|
|
//parse the date / time |
|
var parseTime = d3.timeParse("%m/%d/%y"); |
|
|
|
//set the ranges |
|
var x = d3.scaleTime().range([ 0, width ]); |
|
var y = d3.scaleLinear().range([ height, 0 ]); |
|
|
|
//define the line |
|
var line = d3.line().x(function(d) { |
|
return x(d.date); |
|
}).y(function(d) { |
|
return y(d.units); |
|
}).curve(d3.curveCardinal); |
|
|
|
//append the svg object to the body of the page |
|
//appends a 'group' element to 'svg' |
|
//moves the 'group' element to the top left margin |
|
var svg = d3.select("#svg-container").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 + ")"); |
|
|
|
// format the data |
|
data.forEach(function(d) { |
|
d.date = parseTime(d.date); |
|
d.units = + d.units; |
|
}); |
|
|
|
// Scale the range of the data |
|
x.domain(d3.extent(data, function(d) { |
|
return d.date; |
|
})); |
|
y.domain([ 0, d3.max(data, function(d) { |
|
return d.units; |
|
}) ]); |
|
|
|
// Add the X Axis |
|
var xAxis = svg.append("g").attr("class","xaxis").attr("transform", "translate(0," + height + ")").call( |
|
d3.axisBottom(x).ticks(10).tickFormat(function(d){ |
|
return d.toDateString(); |
|
})); |
|
|
|
// Add the Y Axis |
|
var yAxis = svg.append("g").attr("class","yaxis").call(d3.axisLeft(y)); |
|
|
|
// Add the line path. |
|
svg.append("path").data([ data ]).attr("class", "line").attr("height", (height-margin.top) ).attr("d", |
|
line).attr('stroke', 'green').attr('stroke-width', 2) |
|
.attr('fill', 'none'); |
|
|
|
// declare two dimentional brush |
|
var selectZoom = d3.brush().on("start", startMoving) |
|
.on("brush", brushMoving) |
|
.on("end", brushEnded), |
|
idleTimeout, idleDelay = 350, startSelection, movingDirection = null; |
|
|
|
// force move in X direction |
|
var brushX = function(e, selection) { |
|
movingDirection = "x"; |
|
e.target.move(svg, [ [ selection[0][0], 0 ], |
|
[ selection[1][0], height ] ]); |
|
}; |
|
|
|
// force move in Y direction |
|
var brushY = function(e, selection) { |
|
movingDirection = "y"; |
|
e.target.move(svg, [ [ 0, selection[0][1] ], |
|
[ width, selection[1][1] ] ]); |
|
}; |
|
|
|
// when brush starts moving |
|
// capture mouse position |
|
function startMoving() { |
|
startSelection = d3.event.selection; |
|
movingDirection = null; |
|
} |
|
|
|
// when the brush is moving |
|
// capture direction and move brush in same direction |
|
function brushMoving() { |
|
var selection = d3.event.selection; |
|
if (selection == null) |
|
return; |
|
if (movingDirection == null) { // first occurance |
|
if (startSelection[1][1] != selection[1][1] |
|
|| startSelection[0][1] != selection[0][1]) { |
|
brushY(d3.event, selection); |
|
} else if (startSelection[0][0] != selection[0][0] |
|
|| startSelection[1][0] != selection[1][0]) { |
|
brushX(d3.event, selection); |
|
} |
|
} else { |
|
if (movingDirection === "y") { |
|
if (selection[0][0] != 0 && selection[1][0] != width) |
|
brushY(d3.event, selection); |
|
} else { // movingDirection is x |
|
if (selection[0][1] != 0 && selection[1][1] != height) |
|
brushX(d3.event, selection); |
|
} |
|
} |
|
} |
|
|
|
// when the brush stops moving |
|
function brushEnded() { |
|
var selection = d3.event.selection; |
|
if (!selection) { |
|
if (!idleTimeout) |
|
return idleTimeout = setTimeout(idled, idleDelay); |
|
xScale.domain([ domain.X.min, domain.X.max ]); |
|
yScale.domain([ domain.Y.min, domain.Y.max ]); |
|
} else { |
|
x.domain([ selection[0][0], selection[1][0] ].map( |
|
x.invert, x)); |
|
y.domain([ selection[1][1], selection[0][1] ].map( |
|
y.invert, y)); |
|
} |
|
|
|
d3.select(".xaxis").call( |
|
d3.axisBottom(x).ticks(10).tickFormat(function(d){ |
|
return d.toDateString(); |
|
})); |
|
d3.select(".yaxis").call(d3.axisLeft(y)); |
|
|
|
svg.selectAll(".line").attr("d", line); |
|
|
|
selectZoom.move(svg, null); // clear brush |
|
movingDirection = null; |
|
} |
|
|
|
function idled() { |
|
idleTimeout = null; |
|
} |
|
|
|
svg.call(selectZoom); |
|
|
|
</script> |
|
</body> |