Skip to content

Instantly share code, notes, and snippets.

@1Cr18Ni9
Last active January 16, 2017 05:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 1Cr18Ni9/26d07f33b9bcdef49bcc1099862bae12 to your computer and use it in GitHub Desktop.
Save 1Cr18Ni9/26d07f33b9bcdef49bcc1099862bae12 to your computer and use it in GitHub Desktop.
The Brush
license: mit

All data is generated randomly.

Selectiong on the graph, the data point and specific value will be highlighted.

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0;
font-family: Futura, Arial, sans-serif;
}
.tick line, path.domain{stroke: #555;stroke-width: 1;fill: none;}
.tick text{fill: #555;}
.extent{
fill: red;
opacity: 0.3;
}
.grid line{
fill: none;
stroke: #ccc;
stroke-opacity: 0.7;
stroke-width: 1px;
}
.grid path{
display: none;
}
circle.highlight{
transition: all 0.6s;
stroke-width: 3;
stroke: red;
}
text.show{
/* !important */
opacity: 1 !important;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
function draw(){
var margin = {top: 65, bottom: 55, left: 55, right: 45},
axisPadding = 10,
radius = 3, // circle size
svgWidth = 550,
svgHeight = 400,
gWidth = svgWidth - (margin.left + margin.right),
gHeight = svgHeight - (margin.top + margin.bottom);
var xScale = d3.scale.linear().range([0, gWidth]).domain([0, 20]),
yScale = d3.scale.linear().range([0, gHeight]).domain([60, 0]),
xAxis = d3.svg.axis().ticks(5).scale(xScale).orient('bottom'),
yAxis = d3.svg.axis().ticks(5).scale(yScale).orient('left'),
xGrid = d3.svg.axis().ticks(5)
.scale(xScale).orient('bottom')
.tickSize(gHeight,0).tickFormat(''),
yGrid = d3.svg.axis().ticks(5)
.scale(yScale).orient('left')
.tickSize(-gWidth,0).tickFormat('');
// generate all data randomly
var numGenerator = function(x){
if (arguments.length == 0){
var x = 1;
};
return function(){
return Math.floor(Math.random() * x);
};
};
// a generator range from 0 ~ 49
var f = numGenerator(50);
var data = d3.range(20)
.map(function(d, i){
return {x: i, y: f()};
});
// define svg generator
var lineStyle = 'monotone';
var line = d3.svg.line()
.interpolate(lineStyle)
.x(function(d){return xScale(d.x)})
.y(function(d){return yScale(d.y)});
var area = d3.svg.area()
.interpolate(lineStyle)
.x(function(d){return xScale(d.x)})
.y(function(d){return yScale(d.y)})
.y0(yScale(0));
// here we go
var svg = d3.select('#container')
.append('svg')
.attr('width', svgWidth)
.attr('height', svgHeight)
.attr('class', 'graph');
var xGroup = svg.append('g')
.attr('class', 'xGroup')
.attr('transform', 'translate(' +
[margin.left, margin.top + gHeight + axisPadding] + ')');
var yGroup = svg.append('g')
.attr('class', 'yGroup')
.attr('transform', 'translate(' +
[margin.left - axisPadding, margin.top] + ')');
var grid1 = svg.append('g')
.attr('class', 'grid')
.attr('transform', 'translate(' +
[margin.left, margin.top] + ')');
var grid2 = svg.append('g')
.attr('class', 'grid')
.attr('transform', 'translate(' +
[margin.left, margin.top] + ')');
xGroup.call(xAxis);
yGroup.call(yAxis);
grid1.call(xGrid);
grid2.call(yGrid);
var graph = svg.append('g')
.attr('class', 'graph')
.attr('transform', 'translate(' +
[margin.left, margin.top] + ')');
graph
.append('path')
.datum(data)
.attr('class', 'area')
.attr('d', area)
.attr('fill', 'steelblue')
.attr('fill-opacity', 0.5);
graph
.append('path')
.datum(data)
.attr({
class : 'line',
d : line,
fill : 'none',
stroke : 'blue',
'stroke-width': 1
});
var circles = graph
.selectAll('circle')
.data(data)
.enter()
.append('circle')
.each(function(d,i){
d3.select(this).attr({
cx : xScale(d.x),
cy : yScale(d.y),
fill : 'white',
r : radius,
stroke : 'black',
'stroke-width': 1
})
});
// creat labels with 0 opacity
var labels = graph
.selectAll('text')
.data(data)
.enter()
.append('text')
.each(function(d,i){
d3.select(this)
.attr({
x : xScale(d.x),
y : yScale(d.y),
dy: -6,
dx: -6
})
.style({transition: 'all .5s', opacity: 0})
.text(d.y);
});
// define the brush
var brush = d3.svg.brush()
.x(xScale)
.y(yScale);
// apply the brush to graph selection
brush(graph);
// define event
brush
.on('brushstart', function(){
d3.selectAll('circle.highlight').classed('highlight', false);
d3.selectAll('text.show').classed('show', false);
})
.on('brush', function(){
var ext = brush.extent();
circles.classed('highlight', function(d){
// if circles located in the extent teritory,
// then they should be highlighted (return true)
return (ext[0][0] <= d.x && d.x <= ext[1][0]) &&
(ext[0][1] <= d.y && d.y <= ext[1][1])
});
labels.classed('show', function(d){
return (ext[0][0] <= d.x && d.x <= ext[1][0]) &&
(ext[0][1] <= d.y && d.y <= ext[1][1])
})
})
.on('brushend', function(){
console.log(d3.selectAll('circle.highlight')[0].length)
});
// initial a brush
d3.select('rect.extent')
.attr({
width: gWidth / 2,
height: gHeight / 2,
x: gWidth / 4,
y: gHeight / 4,
});
circles.classed('highlight', function(d){
return xScale(d.x) >= (gWidth / 4) &&
xScale(d.x) <= (gWidth / 4 * 3) &&
yScale(d.y) >= (gHeight / 4) &&
yScale(d.y) <= (gHeight / 4 * 3);
});
labels.classed('show', function(d){
return xScale(d.x) >= (gWidth / 4) &&
xScale(d.x) <= (gWidth / 4 * 3) &&
yScale(d.y) >= (gHeight / 4) &&
yScale(d.y) <= (gHeight / 4 * 3);
});
};
draw();
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment