This simple example comes from my blog on Using gradients for abrupt color changes in data visualizations
Showing how an underlying gradient can be updated when you move the brush over the rectangle
You can see this technique of abrupt color changes applied to an actual data visualization in these two examples
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Dynamically updating gradient</title>
<!-- D3.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<!-- Google Fonts -->
<link href='//fonts.googleapis.com/css?family=Open+Sans:300,400' rel='stylesheet' type='text/css'>
<style>
html { font-size: 62.5%; }
body {
font-family: 'Open Sans', sans-serif;
font-weight: 400;
text-align: center;
fill: #2B2B2B;
}
.brush .extent {
fill-opacity: .125;
shape-rendering: crispEdges;
}
.resize {
display: inline !important; /* show when empty */
fill: #7A7A7A;
stroke: #7A7A7A;
stroke-width: 3px;
}
.stopText {
font-size: 2.2rem;
color: #303030;
text-anchor: middle;
stroke: none;
}
</style>
</head>
<body>
<div id = "chart"></div>
<script>
///////////////////////////////////////////////////////////////////////////
//////////////////// Set up and initiate svg containers ///////////////////
///////////////////////////////////////////////////////////////////////////
var margin = {
top: 50,
right: 30,
bottom: 50,
left: 30
};
var width = window.innerWidth - margin.left - margin.right - 20;
var height = 100;
//SVG container
var svg = d3.select('#chart')
.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 + ")");
//Reset the overall font size
var newFontSize = Math.min(62.5, width * 62.5 / 900);
d3.select("html").style("font-size", newFontSize + "%");
//////////////////////////////////////////////////////////////
////////////////////////// Scales ////////////////////////////
//////////////////////////////////////////////////////////////
var start = 0,
end = 100,
range = end - start;
var xAll = d3.scale.linear().domain([start, end]).range([0, width]),
xBrush = d3.scale.linear().domain([start, end]).range([0, width]);
//////////////////////////////////////////////////////////////
/////////////////////// Gradient /////////////////////////////
//////////////////////////////////////////////////////////////
var linearGradient = svg.append("defs").append("linearGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", width)
.attr("y2", 0)
.attr("id", "line-gradient");
linearGradient.append("stop").attr("class", "left").attr("offset", "40%").attr("stop-color", "#D6D6D6");
linearGradient.append("stop").attr("class", "left").attr("offset", "40%").attr("stop-color", "#BD2E86");
linearGradient.append("stop").attr("class", "right").attr("offset", "60%").attr("stop-color", "#BD2E86");
linearGradient.append("stop").attr("class", "right").attr("offset", "60%").attr("stop-color", "#D6D6D6");
//////////////////////////////////////////////////////////////
/////////////////////// Brushing /////////////////////////////
//////////////////////////////////////////////////////////////
//Taken and adjusted from: //bl.ocks.org/mbostock/6498580
var centering = false,
alpha = 1,
center,
moveType;
var arc = d3.svg.arc()
.outerRadius(height / 4)
.startAngle(0)
.endAngle(function(d, i) { return i ? -Math.PI : Math.PI; });
var brush = d3.svg.brush()
.x(xAll)
.extent([(40*range)/100+start, (60*range)/100+start])
.on("brush", brushmove)
.on("brushend", brushend);;
//Set up the brush
var gBrush = svg.append("g")
.attr("class", "brush")
.style("opacity", 0)
.call(brush);
gBrush.selectAll(".resize").append("line")
.attr("y2", height);
gBrush.selectAll(".resize").append("path")
.attr("d", d3.svg.symbol().type("triangle-up").size(100))
.attr("transform", function(d,i) { return i ? "translate(" + -7 + "," + height / 2 + ") rotate(-90)" : "translate(" + 7 + "," + height / 2 + ") rotate(90)"; });
gBrush.selectAll("rect")
.attr("height", height);
gBrush.select(".background")
.on("mousedown.brush", brushcenter)
.on("touchstart.brush", brushcenter);
gBrush.call(brush.event);
function brushmove() {
var extent = brush.extent();
//Reset the x-axis brush domain
xBrush.domain(brush.empty() ? xAll.domain() : brush.extent());
var startOffset = (xBrush.domain()[0] - start)/range*100,
endOffset = (xBrush.domain()[1] - start)/range*100;
//Update the text below each brush handle
d3.select(".leftStopText").text(Math.round(startOffset) + "%");
d3.select(".rightStopText").text(Math.round(endOffset) + "%");
//Reset the offsets of the chart
d3.selectAll(".left").attr("offset", startOffset + "%");
d3.selectAll(".right").attr("offset", endOffset + "%");
}//brushmove
function brushend() {
if (!d3.event.sourceEvent) return; // only transition after input
d3.select(this).transition()
.call(brush.extent(brush.extent().map(function(d) { return d3.round(d, 0); })))
.call(brush.event);
}//brushend
function brushcenter() {
var self = d3.select(window),
target = d3.event.target,
extent = brush.extent(),
size = extent[1] - extent[0],
domain = xAll.domain(),
x0 = domain[0] + size / 2,
x1 = domain[1] - size / 2,
odd = Math.round(size * 10) & 1;
recenter(true);
brushmove();
if (d3.event.changedTouches) {
self.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
} else {
self.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
}
function brushmove() {
d3.event.stopPropagation();
center = d3.round(Math.max(x0, Math.min(x1, xAll.invert(d3.mouse(target)[0]) + odd * .05)), 1) - odd * .05;
recenter(false);
}
function brushend() {
brushmove();
self.on(".brush", null);
}
}//brushcenter
function recenter() {
if (centering) return; // timer is active and already interpolating
centering = true;
d3.timer(function() {
var extent = brush.extent(),
size = extent[1] - extent[0],
center1 = center * alpha + (extent[0] + extent[1]) / 2 * (1 - alpha);
if (!(centering = Math.abs(center1 - center) > 1e-3)) center1 = center;
gBrush
.call(brush.extent([center1 - size / 2, center1 + size / 2]))
.call(brush.event);
return !centering;
});
}//recenter
//////////////////////////////////////////////////////////////
//////////////////////// Rectangle ///////////////////////////
//////////////////////////////////////////////////////////////
//Add the lines to context chart
svg.append("rect")
.attr("class", "exampleRect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.style("fill", "url(#line-gradient)");
//////////////////////////////////////////////////////////////
//////////////////////// Locations ///////////////////////////
//////////////////////////////////////////////////////////////
//Append two texts below the brush to show at what percentage they are
d3.selectAll(".brush .resize.w").append("text")
.attr("class", "stopText leftStopText")
.attr("x", 0)
.attr("y", 140)
.style("text-anchor", "middle")
.text("40%");
d3.selectAll(".brush .resize.e").append("text")
.attr("class", "stopText rightStopText")
.attr("x", 0)
.attr("y", 140)
.style("text-anchor", "middle")
.text("60%");
//////////////////////////////////////////////////////////////
/////////////////////////// Start ////////////////////////////
//////////////////////////////////////////////////////////////
//Move selected element to the front
d3.selection.prototype.moveToFront = function() {
return this.each(function(){
this.parentNode.appendChild(this);
});
};
//Move the brush handles to the front
setTimeout(function() {
d3.selectAll(".brush")
.style("opacity", 1)
.moveToFront();
},500);
</script>
</body>
</html>