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.
Last active
April 11, 2024 18:53
-
-
Save mbostock/6232537 to your computer and use it in GitHub Desktop.
Brush Snapping
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 | |
redirect: https://observablehq.com/@d3/brush-snapping-transitions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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="https://d3js.org/d3.v4.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 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(d3.brushX() | |
.extent([[0, 0], [width, height]]) | |
.on("end", brushended)); | |
function brushended() { | |
if (!d3.event.sourceEvent) return; // Only transition after input. | |
if (!d3.event.selection) return; // Ignore empty selections. | |
var d0 = d3.event.selection.map(x.invert), | |
d1 = d0.map(d3.timeDay.round); | |
// If empty when rounded, use floor & ceil instead. | |
if (d1[0] >= d1[1]) { | |
d1[0] = d3.timeDay.floor(d0[0]); | |
d1[1] = d3.timeDay.offset(d1[0]); | |
} | |
d3.select(this).transition().call(d3.event.target.move, d1.map(x)); | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment