This brush snaps to day boundaries. On release, the brush fires an end event, allowing a listener to modify the brush selection. Using brush.move to initiate a transition, the brush smoothly interpolates from the original selection to the rounded selection. Compare this approach to using immediate snapping while brushing.
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <style> | |
| .axis--grid .domain { | |
| fill: #ddd; | |
| stroke: none; | |
| } | |
| .axis--x .domain, | |
| .axis--grid .tick line { | |
| stroke: #fff; | |
| } | |
| .axis--grid .tick--minor line { | |
| stroke-opacity: .5; | |
| } | |
| </style> | |
| <body> | |
| <script src="//d3js.org/d3.v4.0.0-alpha.49.min.js"></script> | |
| <script> | |
| var margin = {top: 200, right: 40, bottom: 200, left: 40}, | |
| width = 960 - margin.left - margin.right, | |
| height = 500 - margin.top - margin.bottom; | |
| var x = d3.scaleTime() | |
| .domain([new Date(2013, 7, 1), new Date(2013, 7, 15) - 1]) | |
| .rangeRound([0, width]); | |
| var brush = d3.brushX() | |
| .extent([[0, 0], [width, height]]) | |
| .on("end", brushended); | |
| var svg = d3.select("body").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 + ")"); | |
| svg.append("g") | |
| .attr("class", "axis axis--grid") | |
| .attr("transform", "translate(0," + height + ")") | |
| .call(d3.axisBottom(x) | |
| .ticks(d3.timeHour, 12) | |
| .tickSize(-height) | |
| .tickFormat(function() { return null; })) | |
| .selectAll(".tick") | |
| .classed("tick--minor", function(d) { return d.getHours(); }); | |
| svg.append("g") | |
| .attr("class", "axis axis--x") | |
| .attr("transform", "translate(0," + height + ")") | |
| .call(d3.axisBottom(x) | |
| .ticks(d3.timeDay) | |
| .tickPadding(0)) | |
| .attr("text-anchor", null) | |
| .selectAll("text") | |
| .attr("x", 6); | |
| svg.append("g") | |
| .attr("class", "brush") | |
| .call(brush); | |
| function brushended() { | |
| if (!d3.event.sourceEvent) return; // Only transition after input. | |
| if (!d3.event.selection) return; // Ignore empty selections. | |
| var domain0 = d3.event.selection.map(x.invert), | |
| domain1 = domain0.map(d3.timeDay.round); | |
| // If empty when rounded, use floor & ceil instead. | |
| if (domain1[0] >= domain1[1]) { | |
| domain1[0] = d3.timeDay.floor(domain0[0]); | |
| domain1[1] = d3.timeDay.ceil(domain0[1]); | |
| } | |
| d3.select(this) | |
| .transition() | |
| .call(brush.move, domain1.map(x)); | |
| } | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
