v4 Implementation of a smooth slider with timer and start/pause button. Inspired by Oracle’s Path to Victory, with the help of blocks from Mike Bostock and Jane Pong.
Last active
July 16, 2018 09:57
-
-
Save nl-hugo/7a25bf7e43ca7aa5e8ce15a4032b623c to your computer and use it in GitHub Desktop.
Slider
This file contains 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
height: 250 | |
license: MIT |
This file contains 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
This file contains 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> | |
<meta charset="utf-8"> | |
<style> | |
#play-button { | |
position: absolute; | |
top: 120px; | |
left: 30px; | |
background: #004276; | |
padding-right: 26px; | |
border-radius: 2px; | |
border: none; | |
color: white; | |
margin: 0; | |
padding: 0 12px; | |
width: 68px; | |
cursor: pointer; | |
height: 30px; | |
font: 13px sans-serif; | |
} | |
#play-button:hover { | |
background-color: #064d84; | |
} | |
#play-button:active { | |
background-color: #002657; | |
} | |
.ticks { | |
font: 10px sans-serif; | |
} | |
.track, | |
.track-inset, | |
.track-overlay { | |
stroke-linecap: round; | |
} | |
.track { | |
stroke: #000; | |
stroke-opacity: 0.3; | |
stroke-width: 10px; | |
} | |
.track-inset { | |
stroke: #ddd; | |
stroke-width: 8px; | |
} | |
.track-overlay { | |
pointer-events: stroke; | |
stroke-width: 50px; | |
cursor: crosshair; | |
} | |
.handle { | |
fill: #fff; | |
stroke: #000; | |
stroke-opacity: 0.5; | |
stroke-width: 1.25px; | |
} | |
</style> | |
<button id="play-button">Pause</button> | |
<svg width="960" height="250"></svg> | |
<script src="//d3js.org/d3.v4.min.js"></script> | |
<script> | |
let svg = d3.select("svg"), | |
margin = { right: 50, left: 120 }, | |
width = +svg.attr("width") - margin.left - margin.right, | |
height = +svg.attr("height"); | |
let target = actual = 0, | |
alpha = 0.2, | |
timer = d3.timer(updateTween), | |
stepTimer, | |
moving = false, | |
maxValue = 180 - 1, | |
trailLength = 10; | |
const playButton = d3.select("#play-button"); | |
const x = d3.scaleLinear() | |
.domain([0, 180]) | |
.range([0, width]) | |
.clamp(true); | |
const slider = svg.append("g") | |
.attr("class", "slider") | |
.attr("transform", "translate(" + margin.left + "," + height / 2 + ")"); | |
slider.append("line") | |
.attr("class", "track") | |
.attr("x1", x.range()[0]) | |
.attr("x2", x.range()[1]) | |
.select(function() { return this.parentNode.appendChild(this.cloneNode(true)); }) | |
.attr("class", "track-inset") | |
.select(function() { return this.parentNode.appendChild(this.cloneNode(true)); }) | |
.attr("class", "track-overlay") | |
.call(d3.drag() | |
.on("start.interrupt", () => slider.interrupt()) | |
.on("start drag", () => update(x.invert(d3.event.x)))); | |
slider.insert("g", ".track-overlay") | |
.attr("class", "ticks") | |
.attr("transform", "translate(0," + 18 + ")") | |
.selectAll("text") | |
.data(x.ticks(10)) | |
.enter().append("text") | |
.attr("x", x) | |
.attr("text-anchor", "middle") | |
.text(d => d + "°"); | |
const handle = slider.insert("circle", ".track-overlay") | |
.attr("class", "handle") | |
.attr("r", 9); | |
d3.select(window) | |
.on("keydown", keydowned); | |
playButton | |
.on("click", paused) | |
.each(paused); | |
function update(d) { | |
target = d; | |
moving = true; | |
timer.restart(updateTween); | |
} | |
function updateTween() { | |
let diff = target - actual; | |
if (Math.abs(diff) < 1e-3) actual = target, timer.stop(); | |
else actual += diff * alpha; | |
handle.attr("cx", x(actual)); | |
svg.style("background-color", d3.hsl(actual, 0.8, 0.8)); | |
} | |
function keydowned() { | |
let currentValue = actual; | |
if (d3.event.metaKey || d3.event.altKey) return; | |
switch (d3.event.keyCode) { | |
case 37: currentValue = Math.max(x.domain()[0], actual - trailLength); break; | |
case 39: currentValue = Math.min(x.domain()[1], actual + trailLength); break; | |
default: return; | |
} | |
update(currentValue); | |
paused(); | |
} | |
function paused() { | |
if (moving) { | |
slider.interrupt(); | |
clearInterval(stepTimer); | |
moving = false; | |
playButton.text("Play"); | |
} else { | |
if (actual > maxValue) actual = 0; | |
stepTimer = setInterval(step, 100); | |
moving = true; | |
playButton.text("Pause"); | |
} | |
} | |
function step() { | |
if (actual > maxValue) paused(); | |
else update(actual + trailLength / 10); | |
} | |
</script> |
This file contains 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
{ | |
"name": "slider", | |
"version": "0.0.1", | |
"description": "D3 v4 slider", | |
"keywords": [ | |
], | |
"author": "Hugo Janssen <nl-hugo@hugojanssen.nl>", | |
"private": true, | |
"license": "MIT", | |
"devDependencies": { | |
}, | |
"scripts": { | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment