This is a d3 step slider inspired by Mike Bostock's d3 Slider (https://bl.ocks.org/mbostock/6452972). This instance has an addition of a "STEP" value that makes it work just like a jQuery slider initiated with a step value. Change the step to either null or 0 to get the drag value as is!
Last active
February 11, 2018 21:05
d3 Step Slider
This file contains hidden or 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: mit |
This file contains hidden or 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> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>d3 Slider</title> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<style> | |
#chart { | |
border: 1px solid #ccc; | |
} | |
#chart svg line.track { | |
stroke-opacity: 0.4; | |
stroke: #000; | |
stroke-width: 10px; | |
} | |
#chart svg line.track-inset { | |
stroke: steelblue; | |
stroke-width: 8px; | |
} | |
#chart svg line.track-overlay { | |
stroke: #ccc; | |
stroke-width: 40px; | |
stroke-opacity: 0; | |
cursor: crosshair; | |
} | |
#chart svg .handle { | |
fill: red; | |
stroke: #000; | |
} | |
#chart svg line.track-overlay, #chart svg line.track, #chart svg line.track-inset { | |
stroke-linecap: round; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="chart"> | |
</div> | |
<script type="text/javascript"> | |
var margin = {left: 30, right: 30}, | |
width = 860, | |
height = 500, | |
range = [0, 24], | |
step = 2; // change the step and if null, it'll switch back to a normal slider | |
// append svg | |
var svg = d3.select('div#chart').append('svg') | |
.attr('width', width) | |
.attr('height', height); | |
var slider = svg.append('g') | |
.classed('slider', true) | |
.attr('transform', 'translate(' + margin.left +', '+ (height/2) + ')'); | |
// using clamp here to avoid slider exceeding the range limits | |
var xScale = d3.scaleLinear() | |
.domain(range) | |
.range([0, width - margin.left - margin.right]) | |
.clamp(true); | |
// array useful for step sliders | |
var rangeValues = d3.range(range[0], range[1], step || 1).concat(range[1]); | |
var xAxis = d3.axisBottom(xScale).tickValues(rangeValues).tickFormat(function (d) { | |
return d; | |
}); | |
xScale.clamp(true); | |
// drag behavior initialization | |
var drag = d3.drag() | |
.on('start.interrupt', function () { | |
slider.interrupt(); | |
}).on('start drag', function () { | |
dragged(d3.event.x); | |
}); | |
// this is the main bar with a stroke (applied through CSS) | |
var track = slider.append('line').attr('class', 'track') | |
.attr('x1', xScale.range()[0]) | |
.attr('x2', xScale.range()[1]); | |
// this is a bar (steelblue) that's inside the main "track" to make it look like a rect with a border | |
var trackInset = d3.select(slider.node().appendChild(track.node().cloneNode())).attr('class', 'track-inset'); | |
var ticks = slider.append('g').attr('class', 'ticks').attr('transform', 'translate(0, 4)') | |
.call(xAxis); | |
// drag handle | |
var handle = slider.append('circle').classed('handle', true) | |
.attr('r', 8); | |
// this is the bar on top of above tracks with stroke = transparent and on which the drag behaviour is actually called | |
// try removing above 2 tracks and play around with the CSS for this track overlay, you'll see the difference | |
var trackOverlay = d3.select(slider.node().appendChild(track.node().cloneNode())).attr('class', 'track-overlay') | |
.call(drag); | |
// text to display | |
var text = svg.append('text').attr('transform', 'translate(' + (width/2) + ', ' + height/3 + ')') | |
.text('Value: 0'); | |
// initial transition | |
slider.transition().duration(750) | |
.tween("drag", function () { | |
var i = d3.interpolate(0, 10); | |
return function (t) { | |
dragged(xScale(i(t))); | |
} | |
}); | |
function dragged(value) { | |
var x = xScale.invert(value), index = null, midPoint, cx, xVal; | |
if(step) { | |
// if step has a value, compute the midpoint based on range values and reposition the slider based on the mouse position | |
for (var i = 0; i < rangeValues.length - 1; i++) { | |
if (x >= rangeValues[i] && x <= rangeValues[i + 1]) { | |
index = i; | |
break; | |
} | |
} | |
midPoint = (rangeValues[index] + rangeValues[index + 1]) / 2; | |
if (x < midPoint) { | |
cx = xScale(rangeValues[index]); | |
xVal = rangeValues[index]; | |
} else { | |
cx = xScale(rangeValues[index + 1]); | |
xVal = rangeValues[index + 1]; | |
} | |
} else { | |
// if step is null or 0, return the drag value as is | |
cx = xScale(x); | |
xVal = x.toFixed(3); | |
} | |
// use xVal as drag value | |
handle.attr('cx', cx); | |
text.text('Value: ' + xVal); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment