block by nbremer b1fbcc0ff00abe8893a087d85fc8005b

Abrupt gradients - Dynamically updating gradient

Full Screen

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

index.html

<!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>