Last active
May 4, 2018 13:12
-
-
Save skokenes/80d4dd564656cc939dab521fd1bee7c5 to your computer and use it in GitHub Desktop.
A simple progress bar with RxJS and D3
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
<html> | |
<head> | |
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script> | |
<script src = "https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.1/Rx.min.js"></script> | |
</head> | |
<body> | |
<div id="time"></div> | |
<input type="range"></input> | |
<button id = "play">Play</button> | |
<button id = "pause">Pause</button> | |
<button id = "stop">Stop</button> | |
<button id = "speed1x">1x</button> | |
<button id = "speed2x">2x</button> | |
<script src = "main.js"></script> | |
</body> | |
</html> |
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
// The time duration of our timer | |
var maxDuration = 10000; | |
// d3.timer wrapped in an observable | |
const timer$ = Rx.Observable.create((observer) => { | |
// On subscribe, create a new timer | |
const t = d3.timer(elapsed => { | |
// Pass the elapsed time from the timer | |
observer.next(elapsed); | |
}); | |
// Stop the timer when unsubscribed | |
return t.stop; | |
}); | |
// Create click events for the play, pause, stop, and speed buttons | |
const play$ = Rx.Observable.fromEvent(document.querySelector("#play"), "click"); | |
const pause$ = Rx.Observable.fromEvent(document.querySelector("#pause"), "click"); | |
const stop$ = Rx.Observable.fromEvent(document.querySelector("#stop"), "click"); | |
// Map to speed 1x button to the value 1 on click | |
const speed1x$ = Rx.Observable.fromEvent(document.querySelector("#speed1x"), "click") | |
.mapTo(1); | |
// Map the speed 2x button to the value 2 on click | |
const speed2x$ = Rx.Observable.fromEvent(document.querySelector("#speed2x"), "click") | |
.mapTo(2); | |
// Create the change event for the slider for when a person clicks on the timer. | |
// Map it to the new value of the slider and convert that to a position on our timeline | |
var slider = document.querySelector("input"); | |
var sliderChange$ = Rx.Observable.fromEvent(slider, "change") | |
.map(evt=>evt.target.value / 100 * maxDuration); | |
// Merge the speeds together into 1 stream, and start with 1 by default | |
const speed$ = speed1x$ | |
.merge(speed2x$) | |
.startWith(1); | |
// An observable of new times to jump to based on either stopping or clicking the timer range | |
const scrub$ = stop$.mapTo(0) | |
.merge(sliderChange$); | |
// Whenever someone presses play, create a new timer and calculate the intervals between each elapsed time | |
const interval$ = play$.switchMap(() => | |
timer$ | |
.startWith(0) // start with 0 | |
.pairwise() // emit the last 2 values at a time in an array | |
.map(([a, b]) => b - a) // calculate the difference between the last two values | |
.combineLatest(speed$, (interval, speed) => interval * speed) // multiply the interval by the current speed | |
.takeUntil(pause$.merge(stop$)) // emit these timer intervals until a pause or stop event | |
); | |
// Use the interval stream with the scrub stream to either add time or jump in time | |
const time$ = interval$ | |
.map(m => (v) => v + m) // When interval stream fires, pass a function that takes previous value and adds latest interval | |
.merge(scrub$.map(m => (v) => m)) // When the scrub stream fires, jump to the new value | |
.startWith(0) // start the time at 0 | |
.scan((val, fn) => fn(val)) // for each value, apply the latest function that fired (either increment or jump) | |
.filter(t=> t <= maxDuration); // only take times within our max duration range | |
// Subscribe: Print the time and update the slider position | |
time$.subscribe(t=>{ | |
document.querySelector("#time").innerHTML = t | |
slider.value = t / maxDuration * 100; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment