|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<meta title="Minimal D3 v4 multiple brushes example--Ludwig Schubert for Stanford CS448b"> |
|
<style> |
|
|
|
.grid-background { |
|
fill: #eee; |
|
} |
|
|
|
.grid line, |
|
.grid path { |
|
fill: none; |
|
stroke: #000; |
|
shape-rendering: crispEdges; |
|
} |
|
|
|
|
|
.brush .selection { |
|
stroke: #000; |
|
fill: red; |
|
} |
|
|
|
</style> |
|
<body> |
|
|
|
<script src="http://d3js.org/d3.v4.min.js"></script> |
|
|
|
<script> |
|
var margin = { |
|
top: 10, |
|
right: 10, |
|
bottom: 10, |
|
left: 10 |
|
}, |
|
width = 960 - margin.left - margin.right, |
|
height = 500 - margin.top - margin.bottom; |
|
|
|
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("rect") |
|
.attr("class", "grid-background") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
// We initially generate a SVG group to keep our brushes' DOM elements in: |
|
var gBrushes = svg.append('g') |
|
.attr("class", "brushes"); |
|
|
|
// We also keep the actual d3-brush functions and their IDs in a list: |
|
var brushes = []; |
|
|
|
/* CREATE NEW BRUSH |
|
* |
|
* This creates a new brush. A brush is both a function (in our array) and a set of predefined DOM elements |
|
* Brushes also have selections. While the selection are empty (i.e. a suer hasn't yet dragged) |
|
* the brushes are invisible. We will add an initial brush when this viz starts. (see end of file) |
|
* Now imagine the user clicked, moved the mouse, and let go. They just gave a selection to the initial brush. |
|
* We now want to create a new brush. |
|
* However, imagine the user had simply dragged an existing brush--in that case we would not want to create a new one. |
|
* We will use the selection of a brush in brushend() to differentiate these cases. |
|
*/ |
|
function newBrush() { |
|
var brush = d3.brush() |
|
.on("start", brushstart) |
|
.on("brush", brushed) |
|
.on("end", brushend); |
|
|
|
brushes.push({id: brushes.length, brush: brush}); |
|
|
|
function brushstart() { |
|
// your stuff here |
|
}; |
|
|
|
function brushed() { |
|
// your stuff here |
|
} |
|
|
|
function brushend() { |
|
|
|
// Figure out if our latest brush has a selection |
|
var lastBrushID = brushes[brushes.length - 1].id; |
|
var lastBrush = document.getElementById('brush-' + lastBrushID); |
|
var selection = d3.brushSelection(lastBrush); |
|
|
|
// If it does, that means we need another one |
|
if (selection && selection[0] !== selection[1]) { |
|
newBrush(); |
|
} |
|
|
|
// Always draw brushes |
|
drawBrushes(); |
|
} |
|
} |
|
|
|
function drawBrushes() { |
|
|
|
var brushSelection = gBrushes |
|
.selectAll('.brush') |
|
.data(brushes, function (d){return d.id}); |
|
|
|
// Set up new brushes |
|
brushSelection.enter() |
|
.insert("g", '.brush') |
|
.attr('class', 'brush') |
|
.attr('id', function(brush){ return "brush-" + brush.id; }) |
|
.each(function(brushObject) { |
|
//call the brush |
|
brushObject.brush(d3.select(this)); |
|
}); |
|
|
|
/* REMOVE POINTER EVENTS ON BRUSH OVERLAYS |
|
* |
|
* This part is abbit tricky and requires knowledge of how brushes are implemented. |
|
* They register pointer events on a .overlay rectangle within them. |
|
* For existing brushes, make sure we disable their pointer events on their overlay. |
|
* This frees the overlay for the most current (as of yet with an empty selection) brush to listen for click and drag events |
|
* The moving and resizing is done with other parts of the brush, so that will still work. |
|
*/ |
|
brushSelection |
|
.each(function (brushObject){ |
|
d3.select(this) |
|
.attr('class', 'brush') |
|
.selectAll('.overlay') |
|
.style('pointer-events', function() { |
|
var brush = brushObject.brush; |
|
if (brushObject.id === brushes.length-1 && brush !== undefined) { |
|
return 'all'; |
|
} else { |
|
return 'none'; |
|
} |
|
}); |
|
}) |
|
|
|
brushSelection.exit() |
|
.remove(); |
|
} |
|
|
|
newBrush(); |
|
drawBrushes(); |
|
|
|
</script> |