An event source built around an observer design pattern implementation…
var observableValue = function(value) {
return ( function() {
var observed = value;
var get = function () { return observed; };
var set = function (newValue) { observed = newValue; };
var withdraw = function (value) {
var once = function(values) {
var index = values.indexOf(value);
if ( index > -1 ) {
values.splice(index, 1);
return true;
}
return false;
}
var remover = once;
return { from: function(values) {
return remover(values);
}
, entierly: function() {
remover = function(values) {
var index = values.indexOf(value);
if ( index == -1 ) { return false; }
for ( var i = 0; i < values.length; i++ ) {
if ( values[i] === value ) {
values.splice(i, 1);
}
}
return true;
};
return this;
}
, once: function() {
remover = once;
return this;
}};
};
return { value: get
, observers: []
, oneTimeObservers: []
, is: function(value) {
var priorValue = get();
set(value);
this.updateObservers(priorValue, value);
}
, updateObservers: function(priorValue, value) {
var context = this;
setTimeout( () => {
context.observers.forEach((observer) => {
observer.update.call(observer, priorValue, value);
});
var oneTimeObservers = context.oneTimeObservers;
for ( var i = 0; i < oneTimeObservers.length; i++ ) {
var observer = oneTimeObservers.pop();
if ( !observer.update.call(observer
, priorValue
, value) ) {
context.oneTimeObservers.push(observer);
}
}
}, 1);
}
, observedBy: function(observer) {
this.observers.push(observer);
return true;
}
, oneTimeObservedBy: function(observer) {
this.oneTimeObservers.push(observer);
return true;
}
, withdrawObserver: function(observer) {
return withdraw(observer).from(this.observers);
}
, withdrawOneTimeObserver: function(observer) {
return withdraw(observer).from(this.oneTimeObservers);
}};
})();};
…generating events in the interval from zero to a configurable value that could be changed during runtime…
var rotor = function(area, numberOfSteps, waver, stepDurationsMillis) {
var gear = { supply: function(handler) {
var next = observableValue(0);
var activeHandlerIndex = 0;
var neutral = () => { return null; };
var handlers = [neutral];
var handle = { it: function(handlee) {
if ( handlee ) { return handlers.push(handlee); }
return false;
} };
(function(handlee) { handle.it(handlee); })(handler);
return {
handled: { by: function(handler) {
return (handlers.length == 2) ? false : handle.it(handler) && true;
} }
, activeHandler: function() {
return handlers[activeHandlerIndex];
}
, couple: function(next, context, stepDurationMillis) {
var handler = this.activeHandler()
, handlerContext = this;
return handler.call(handlerContext, next, context, stepDurationMillis);
}
, shift: { it: function() {
activeHandlerIndex = (activeHandlerIndex + 1) % handlers.length;
}
, to: { neutral: function() { activeHandlerIndex = 0; } }
}
, isNeutral: function() { return activeHandlerIndex == 0; }
, stepObservedBy: function(observer) { next.observedBy(observer); }
, withdrawStepObserver: function(observer) { next.withdrawObserver(observer); }
, stepValueIs: function(step) { next.is(step); }
, stepValue: function() { return next.value(); }
};
} };
var visualizer = function(numberOfValues, visual) {
var valueShades = (function(numberOfShades) {
var shades = []
, shadeStep = Math.floor(50 / numberOfShades);
for ( var i = 0; i < numberOfShades; i++ ) {
shades.push("hsl(205, 20%, " + (50 + shadeStep * i) + "%)");
}
return shades;
})(numberOfValues - 1).concat(["transparent"]);
return {
visual: visual
, valueShades: valueShades
, visual: visual
, update: function(previousIndex, activeIndex) {
this.visual.style.backgroundColor = this.valueShades[activeIndex];
}
};
};
var dot = (function() {
var dot = document.createElement("div");
dot.classList.add("dot");
return dot;
})();
var computeOrbitSteps = function(numberOfSteps) {
var steps = []
, radians = ((360 / numberOfSteps) * 2 * Math.PI) / 360
, cos = Math.cos(radians)
, sin = Math.sin(radians)
, radius = Math.round(area.offsetWidth / 2)
, originX = radius
, originY = radius
, margin = radius - Math.round(radius / 2)
, x1 = radius - margin
, y1 = radius - margin;
for ( var i = 0; i < numberOfSteps; i++ ) {
var x2 = (Math.round(x1 * cos - y1 * sin))
, y2 = (Math.round(y1 * cos + x1 * sin))
, x = originX + x2
, y = originY + y2;
steps.push([x, y]);
x1 = x2;
y1 = y2;
}
return steps;
};
var orbitSteps = computeOrbitSteps(numberOfSteps);
var stepDurationsMillisArray = [stepDurationsMillis].reduce(
(accumulator, element) => accumulator.concat(element), []
);
var handler = function(nextStep, context, durationMillis) {
this.stepValueIs(nextStep);
return setTimeout( () => { context.step.call(context, nextStep); }
, durationMillis );
};
var coupler = gear.supply(handler);
var observableStepDurationIndex = observableValue();
var visualHint = visualizer(stepDurationsMillisArray.length, area);
observableStepDurationIndex.observedBy(visualHint);
return {
orbiter: dot
, rotorArea: area
, orbitSteps: orbitSteps
, waver: waver
, inMotion: observableValue()
, timedStepsId: []
, stepDurationsMillisArray: stepDurationsMillisArray
, coupler: coupler
, stepDurationIndex: observableStepDurationIndex
, visualHint: visualHint
, stepDurationIndexObservedBy: function(observer) {
this.stepDurationIndex.observedBy(observer);
}
, stepObservedBy: function(observer) {
this.coupler.stepObservedBy(observer);
}
, computeOrbitSteps: computeOrbitSteps
, on: function() {
this.stepDurationIndex.is(0);
this.resume();
this.rotorArea.appendChild(this.orbiter);
}
, orbitStepsAre: function(numberOfSteps) {
this.off();
while (!this.isPaused()) {
this.pauseOrShift();
}
this.orbitSteps = this.computeOrbitSteps(numberOfSteps);
this.coupler.stepValueIs(this.coupler.stepValue() % numberOfSteps);
this.resume();
}
, off: function() {
this.pause();
for ( var i = 0; i < this.timedStepsId.length; i++ ) {
clearTimeout(this.timedStepsId[i]);
}
}
, resume: function() {
this.coupler.shift.it();
this.step(this.coupler.stepValue());
this.inMotion.is(true);
}
, pause: function() {
this.coupler.shift.to.neutral();
this.inMotion.is(false);
}
, isPaused: function() {
return this.coupler.isNeutral();
}
, pauseOrShift: function() {
if ( this.isPaused() ) {
this.resume();
return;
}
var stepMillisLength = this.stepDurationsMillisArray.length;
var durationIndex = 0;
if ( this.stepDurationIndex.value() == stepMillisLength - 1 ) {
this.pause();
} else {
durationIndex = (this.stepDurationIndex.value() + 1) % stepMillisLength;
}
this.stepDurationIndex.is(durationIndex);
}
, motionObservedBy: function(observer) {
this.inMotion.observedBy(observer);
}
, withdrawMotionObserver: function(observer) {
return this.inMotion.withdrawObserver(observer);
}
, step: function(step) {
this.timedStepsId.pop();
this.orbiter.style.left = this.orbitSteps[step][0];
this.orbiter.style.top = this.orbitSteps[step][1];
var context = this
, nextStep = this.waver.nextStep(step)
, timedStepId = this.coupler
.couple(nextStep
, context
, this.stepDurationsMillisArray[
this.stepDurationIndex.value()
]
);
if ( timedStepId ) { this.timedStepsId.push(timedStepId); }
}
};
};
…in the direction given by the set of strategy design pattern implementations passed among the arguments (waver
argument from previous code snippet that is of type wobbler
see showcase implementation for its implementation details) supporting direction sensitive transition between disjunctive subintervals when the used strategy and the strategy to be used generates values in subintervals.
Technically it is a rotor
taking observers for the step
event it generates for a sequence of values between zero and including the numberOfSteps
argument, in the direction retrieved from an argument of type wobbler
with configurable frequency or frequencies, while visually representing the position of the value in the interval using precomputed coordinates for a graphical pointer that it is placed on the HTMLElement
area
argument and also the frequency of event generation using a visualizer
that changes the background-color
of the afore mentioned area
argument. Along with step
event it also generates events for pausing and for event generation frequency change. An additional event it is generated by the wobbler
for changing the orbit, the direction of value generation strategy.
Following a showcase of features it supports responsive to click, double click, mouse down and mouse up mouse events…
<html>
<head>
<style>
body { height: 100%; margin: 0; padding: 0; width: 100%; }
.loading .whirl { height: 120px; width: 120px; }
.loading .whirl:hover, .loading .cluster:hover { box-shadow: none; }
.loading .roller { opacity: 0; }
.loading .bottom-lane { bottom: 0; height: 50%; left: 0; margin: auto; right: 0; top: 0; }
.loading .art-picker { opacity: 1; visibility: visible; }
.loading .cluster { width: 150px; }
.loading .cluster .alternative-text { color: #888; display: inline; font-size: 24px; opacity: 1; position: relative; top: 40%; width: 100%; }
.loading .cluster-hover { background-image: none; }
.loading .cluster-art { background-image: none; }
.loading .key, .loading .key-frame { display: none; opacity: 0; }
.art-transition {}
.key { transition: opacity 1s linear; }
.key-frame { position:relative; height: 100%; width: 100%; }
.key-wrap {}
.cluster { background-color: #ddd; background-position: center; background-repeat: no-repeat; background-size: contain; cursor: pointer; display: inline-block; height: 100%; margin-right: 5px; width: 90px; transition: background-image 1s linear, box-shadow 1s linear; }
.cluster .alternative-text { display: none; }
.cluster-hover {}
.cluster-hover .key-wrap { height: 70%; width: 70%; }
.cluster-hover .key { border-radius: 50%; box-shadow: 0px 0px 10px 5px #fff; height: 80%; width: 100%; }
.cluster-art {}
.cluster-art .key-wrap { height: 85%; width: 80%; }
.cluster-art .key { color: #fdfdfd; float: left; height: 27%; text-align: center; width: 32%; }
.centered { bottom: 0; left: 0; margin: auto; position: absolute; right: 0; top: 0; }
.sized { max-height: 400px; max-width: 400px; min-height: 30%; min-width: 30%; }
.art-picker { height: 50%; margin: 5px; text-align: center; opacity: 0; visibility: hidden; transition: opacity 1s linear, visibility 1s linear; }
.bottom-lane { background-color: transparent; bottom: 0; height: 210px; position: absolute; width: 100%; transition: margin 1s linear, height 1s linear; }
.whirl { background-color: #778899; border-radius: 50%; cursor: pointer; height: 80px; position: relative; transition: background-color 1s linear, box-shadow 1s linear; width: 80px; }
.whirl:hover, .cluster:hover { box-shadow: 0px 0px 10px 5px #fff, 0px 5px 20px 0 #778899; }
.dot { background-color: #dfdfdf; border-radius: 50% 50% 50% 50%; height: 3px; position: absolute; width: 3px; }
.alternative-text { display: inline; opacity: 0; }
.hover.paused { box-shadow: 0px 0px 5px 0px #fff, 0px 0px 100px 0px #778899; }
.hover .symbol { display: none; }
.hover .minute { border-top: 30px solid #777; border-bottom: 30px solid #777; background-color: transparent; position: absolute; width: 2px; height: 400px; }
.hover .minute.quarter { height: 370px; border-top: 45px solid #777; border-bottom: 45px solid #777; }
.hover .minute.fifth { height: 380px; border-top: 40px solid #777; border-bottom: 40px solid #777; }
.hover .minute .stage { background-color: transparent; height: 50%; position: absolute; width: 100%; }
.hover .zero {}
.hover .one { transform:rotate(6deg); }
.hover .two { transform:rotate(12deg); }
.hover .three { transform:rotate(18deg); }
.hover .four { transform:rotate(24deg); }
.hover .five { transform:rotate(30deg); }
.hover .six { transform:rotate(36deg); }
.hover .seven { transform:rotate(42deg); }
.hover .eight { transform:rotate(48deg); }
.hover .nine { transform:rotate(54deg); }
.hover .ten { transform:rotate(60deg); }
.hover .eleven { transform:rotate(66deg); }
.hover .twelve { transform:rotate(72deg); }
.hover .thirteen { transform:rotate(78deg); }
.hover .fourteen { transform:rotate(84deg); }
.hover .fifteen { transform:rotate(90deg); }
.hover .sixteen { transform:rotate(96deg); }
.hover .seventeen { transform:rotate(102deg); }
.hover .eighteen { transform:rotate(108deg); }
.hover .nineteen { transform:rotate(114deg); }
.hover .twenty { transform:rotate(120deg); }
.hover .twentyone { transform:rotate(126deg); }
.hover .twentytwo { transform:rotate(132deg); }
.hover .twentythree { transform:rotate(138deg); }
.hover .twentyfour { transform:rotate(144deg); }
.hover .twentyfive { transform:rotate(150deg); }
.hover .twentysix { transform:rotate(156deg); }
.hover .twentyseven { transform:rotate(162deg); }
.hover .twentyeight { transform:rotate(168deg); }
.hover .twentynine { transform:rotate(174deg); }
.hover.south-0 .zero
, .hover.south-1 .one
, .hover.south-2 .two
, .hover.south-3 .three
, .hover.south-4 .four
, .hover.south-5 .five
, .hover.south-6 .six
, .hover.south-7 .seven
, .hover.south-8 .eight
, .hover.south-9 .nine
, .hover.south-10 .ten
, .hover.south-11 .eleven
, .hover.south-12 .twelve
, .hover.south-13 .thirteen
, .hover.south-14 .fourteen
, .hover.south-15 .fifteen
, .hover.south-16 .sixteen
, .hover.south-17 .seventeen
, .hover.south-18 .eighteen
, .hover.south-19 .nineteen
, .hover.south-20 .twenty
, .hover.south-21 .twentyone
, .hover.south-22 .twentytwo
, .hover.south-23 .twentythree
, .hover.south-24 .twentyfour
, .hover.south-25 .twentyfive
, .hover.south-26 .twentysix
, .hover.south-27 .twentyseven
, .hover.south-28 .twentyeight
, .hover.south-29 .twentynine { border-bottom-color: orangered; }
.hover.south-0 .zero .stage
, .hover.south-1 .one .stage
, .hover.south-2 .two .stage
, .hover.south-3 .three .stage
, .hover.south-4 .four .stage
, .hover.south-5 .five .stage
, .hover.south-6 .six .stage
, .hover.south-7 .seven .stage
, .hover.south-8 .eight .stage
, .hover.south-9 .nine .stage
, .hover.south-10 .ten .stage
, .hover.south-11 .eleven .stage
, .hover.south-12 .twelve .stage
, .hover.south-13 .thirteen .stage
, .hover.south-14 .fourteen .stage
, .hover.south-15 .fifteen .stage
, .hover.south-16 .sixteen .stage
, .hover.south-17 .seventeen .stage
, .hover.south-18 .eighteen .stage
, .hover.south-19 .nineteen .stage
, .hover.south-20 .twenty .stage
, .hover.south-21 .twentyone .stage
, .hover.south-22 .twentytwo .stage
, .hover.south-23 .twentythree .stage
, .hover.south-24 .twentyfour .stage
, .hover.south-25 .twentyfive .stage
, .hover.south-26 .twentysix .stage
, .hover.south-27 .twentyseven .stage
, .hover.south-28 .twentyeight .stage
, .hover.south-29 .twentynine .stage { bottom: 0; background-color: orangered; }
.hover.north-0 .zero
, .hover.north-1 .one
, .hover.north-2 .two
, .hover.north-3 .three
, .hover.north-4 .four
, .hover.north-5 .five
, .hover.north-6 .six
, .hover.north-7 .seven
, .hover.north-8 .eight
, .hover.north-9 .nine
, .hover.north-10 .ten
, .hover.north-11 .eleven
, .hover.north-12 .twelve
, .hover.north-13 .thirteen
, .hover.north-14 .fourteen
, .hover.north-15 .fifteen
, .hover.north-16 .sixteen
, .hover.north-17 .seventeen
, .hover.north-18 .eighteen
, .hover.north-19 .nineteen
, .hover.north-20 .twenty
, .hover.north-21 .twentyone
, .hover.north-22 .twentytwo
, .hover.north-23 .twentythree
, .hover.north-24 .twentyfour
, .hover.north-25 .twentyfive
, .hover.north-26 .twentysix
, .hover.north-27 .twentyseven
, .hover.north-28 .twentyeight
, .hover.north-29 .twentynine { border-top-color: orangered; }
.hover.north-0 .zero .stage
, .hover.north-1 .one .stage
, .hover.north-2 .two .stage
, .hover.north-3 .three .stage
, .hover.north-4 .four .stage
, .hover.north-5 .five .stage
, .hover.north-6 .six .stage
, .hover.north-7 .seven .stage
, .hover.north-8 .eight .stage
, .hover.north-9 .nine .stage
, .hover.north-10 .ten .stage
, .hover.north-11 .eleven .stage
, .hover.north-12 .twelve .stage
, .hover.north-13 .thirteen .stage
, .hover.north-14 .fourteen .stage
, .hover.north-15 .fifteen .stage
, .hover.north-16 .sixteen .stage
, .hover.north-17 .seventeen .stage
, .hover.north-18 .eighteen .stage
, .hover.north-19 .nineteen .stage
, .hover.north-20 .twenty .stage
, .hover.north-21 .twentyone .stage
, .hover.north-22 .twentytwo .stage
, .hover.north-23 .twentythree .stage
, .hover.north-24 .twentyfour .stage
, .hover.north-25 .twentyfive .stage
, .hover.north-26 .twentysix .stage
, .hover.north-27 .twentyseven .stage
, .hover.north-28 .twentyeight .stage
, .hover.north-29 .twentynine .stage { background-color: orangered; top: 0; }
.art .minute { border: none; background-color: transparent; height: 100%; transform: none; width: 100%; }
.art .minute .symbol, .art .minute .stage { font-size: 100px; height: 50%; text-align: center; visibility: hidden; width: 100%; bottom: 0; left: 0; margin: auto; position: absolute; right: 0; top: 0; }
.art .minute .stage { height: 100%; width: 100%; }
.art .minute .stage .symbol { visibility: inherit; }
.art.north-0 .zero .symbol
, .art.north-1 .one .symbol
, .art.north-2 .two .symbol
, .art.north-3 .three .symbol
, .art.north-4 .four .symbol
, .art.north-5 .five .symbol
, .art.north-6 .six .symbol
, .art.north-7 .seven .symbol
, .art.north-8 .eight .symbol
, .art.north-9 .nine .symbol
, .art.north-10 .ten .symbol
, .art.north-11 .eleven .symbol
, .art.north-12 .twelve .symbol
, .art.north-13 .thirteen .symbol
, .art.north-14 .fourteen .symbol
, .art.north-15 .fifteen .symbol
, .art.north-16 .sixteen .symbol
, .art.north-17 .seventeen .symbol
, .art.north-18 .eighteen .symbol
, .art.north-19 .nineteen .symbol
, .art.north-20 .twenty .symbol
, .art.north-21 .twentyone .symbol
, .art.north-22 .twentytwo .symbol
, .art.north-23 .twentythree .symbol
, .art.north-24 .twentyfour .symbol
, .art.north-25 .twentyfive .symbol
, .art.north-26 .twentysix .symbol
, .art.north-27 .twentyseven .symbol
, .art.north-28 .twentyeight .symbol
, .art.north-29 .twentynine .symbol { visibility: visible; }
.art.north-0 .stage .symbol
, .art.north-1 .stage .symbol
, .art.north-2 .stage .symbol
, .art.north-3 .stage .symbol
, .art.north-4 .stage .symbol
, .art.north-5 .stage .symbol
, .art.north-6 .stage .symbol
, .art.north-7 .stage .symbol
, .art.north-8 .stage .symbol
, .art.north-9 .stage .symbol
, .art.north-10 .stage .symbol
, .art.north-11 .stage .symbol
, .art.north-12 .stage .symbol
, .art.north-13 .stage .symbol
, .art.north-14 .stage .symbol
, .art.north-15 .stage .symbol
, .art.north-16 .stage .symbol
, .art.north-17 .stage .symbol
, .art.north-18 .stage .symbol
, .art.north-19 .stage .symbol
, .art.north-20 .stage .symbol
, .art.north-21 .stage .symbol
, .art.north-22 .stage .symbol
, .art.north-23 .stage .symbol
, .art.north-24 .stage .symbol
, .art.north-25 .stage .symbol
, .art.north-26 .stage .symbol
, .art.north-27 .stage .symbol
, .art.north-28 .stage .symbol
, .art.north-29 .stage .symbol { visibility: hidden; }
.art.south-0 .zero .stage
, .art.south-1 .one .stage
, .art.south-2 .two .stage
, .art.south-3 .three .stage
, .art.south-4 .four .stage
, .art.south-5 .five .stage
, .art.south-6 .six .stage
, .art.south-7 .seven .stage
, .art.south-8 .eight .stage
, .art.south-9 .nine .stage
, .art.south-10 .ten .stage
, .art.south-11 .eleven .stage
, .art.south-12 .twelve .stage
, .art.south-13 .thirteen .stage
, .art.south-14 .fourteen .stage
, .art.south-15 .fifteen .stage
, .art.south-16 .sixteen .stage
, .art.south-17 .seventeen .stage
, .art.south-18 .eighteen .stage
, .art.south-19 .nineteen .stage
, .art.south-20 .twenty .stage
, .art.south-21 .twentyone .stage
, .art.south-22 .twentytwo .stage
, .art.south-23 .twentythree .stage
, .art.south-24 .twentyfour .stage
, .art.south-25 .twentyfive .stage
, .art.south-26 .twentysix .stage
, .art.south-27 .twentyseven .stage
, .art.south-28 .twentyeight .stage
, .art.south-29 .twentynine .stage { visibility: visible; }
.roller.slow { transition: opacity 1s linear 1s, background-color linear .4s, box-shadow 1s linear; }
.roller.moderate { transition: opacity 1s linear 1s, background-color linear .25s; }
.roller.fast { transition: opacity 1s linear 1s, background-color linear .05s; }
.roller { background-color: transparent; border-radius: 50%; background-position: center; background-repeat: no-repeat; background-size: contain; opacity: .2; transition: opacity 1s linear 1s; }
.roller.art-transition { transition: background-image 1s linear; }
.roller a { display: none; }
.roller.art .art-clock-animation { display: inline-block; }
.roller.art .art-clock-animation { display: inline-block; }
.roller.slow.hover .hover-slow-animation { display: inline-block; }
.roller.fast.hover .hover-fast-animation { display: inline-block; }
.shed { background-color: grey; cursor: auto; height: 100%; opacity: .3; position: absolute; width: 100%; }
.overlay { bottom: 0; margin: auto; position: absolute; right: 0; height: 100%; width: 100%; }
.transparent { opacity: 0; }
.visible { opacity: 1; visibility: visible; }
.image-preloader { height: 0; overflow: hidden; visibility: hidden; width: 0; }
</style>
<script type="text/javascript">
var hoverSequence = ["hover south-0", "hover south-1", "hover south-2", "hover south-3", "hover south-4", "hover south-5", "hover south-6", "hover south-7"
, "hover south-8", "hover south-9", "hover south-10", "hover south-11", "hover south-12", "hover south-13", "hover south-14", "hover south-15"
, "hover south-16", "hover south-17", "hover south-18", "hover south-19", "hover south-20", "hover south-21", "hover south-22", "hover south-23"
, "hover south-24", "hover south-25", "hover south-26", "hover south-27", "hover south-28", "hover south-29", "hover north-0", "hover north-1"
, "hover north-2", "hover north-3", "hover north-4", "hover north-5", "hover north-6", "hover north-7", "hover north-8", "hover north-9"
, "hover north-10", "hover north-11", "hover north-12", "hover north-13", "hover north-14", "hover north-15", "hover north-16", "hover north-17"
, "hover north-18", "hover north-19", "hover north-20", "hover north-21", "hover north-22", "hover north-23", "hover north-24", "hover north-25"
, "hover north-26", "hover north-27", "hover north-28", "hover north-29"]
, artSequence = ["art south-0", "art south-1", "art south-2", "art south-3", "art south-4", "art south-5", "art south-6", "art south-7"
, "art south-8", "art south-9", "art south-10", "art south-11", "art south-12", "art south-13", "art south-14", "art south-15"
, "art south-16", "art south-17", "art south-18", "art south-19", "art south-20", "art south-21", "art south-22", "art south-23"
, "art south-24", "art south-25", "art south-26", "art south-27", "art south-28", "art south-29", "art north-0", "art north-1"
, "art north-2", "art north-3", "art north-4", "art north-5", "art north-6", "art north-7", "art north-8", "art north-9"
, "art north-10", "art north-11", "art north-12", "art north-13", "art north-14", "art north-15", "art north-16", "art north-17"
, "art north-18", "art north-19", "art north-20", "art north-21", "art north-22", "art north-23", "art north-24", "art north-25"
, "art north-26", "art north-27", "art north-28", "art north-29"]
, sequence = hoverSequence
, observer = { update: function(priorValue, activeValue) { return true; } };
var element = { of: (selector) => document.querySelector(selector) };
var threshold = (function() {
return { after : (betweenShifts) => {
var millisSinceShift = Date.now();
return {
millis : () => { return {
or : (alternativeFunction) => {
return (shiftFunction) => {
if ( Date.now() - millisSinceShift >= betweenShifts ) {
millisSinceShift = Date.now();
shiftFunction();
return;
}
millisSinceShift = Date.now();
setTimeout( () => { alternativeFunction(); }, 0);
};
}
} }
, taps : () => {
return { inMillis : (millisBetweenTaps) => {
return { or : (alternativeFunction) => {
var counter = (function(from) {
var tapCounter = from;
return { incremented : () => { return ++tapCounter; }
, decremented : () => { return --tapCounter; }
, counted : () => { return tapCounter; }
, reset : () => { tapCounter = from; }
};
})(1);
return (shiftFunction) => {
if ( Date.now() - millisSinceShift >= millisBetweenTaps ) {
millisSinceShift = Date.now();
counter.reset();
}
if ( counter.counted() == betweenShifts ) {
setTimeout(() => { alternativeFunction(); }, 0);
return;
}
if ( counter.incremented() == betweenShifts ) {
shiftFunction();
}
};
} };
}
};
}
};
} };
})();
var buffered = { source: function(bufferee) {
var enabler = { supply: (purpose) => {
var vain = purpose ? () => purpose() : () => false;
var enablers = [vain];
return { shift: function() { return enablers[0](); }
, schedule: function(schedulee) {
enablers.unshift( () => { enablers.shift(); return schedulee(); } );
return this;
}
};
} };
var returnee = bufferee();
var singularity = enabler.supply(() => returnee);
if ( !returnee ) { singularity.schedule(() => { returnee = bufferee(); return returnee; }); }
return { element: () => singularity.shift()
, reset: () => { returnee = bufferee(); } };
} };
function removeCssClassName(toRemoveFrom, classNames) {
var index = -1
, className = " " + toRemoveFrom.className + " ";
for ( var i = 0; i < classNames.length; i++ ) {
var toRemove = " " + classNames[i] + " ";
if ( className.indexOf(toRemove) > -1 ) {
index = i;
toRemoveFrom.className = className.replace(toRemove, " ");
break;
}
}
return index;
}
function joinCssClassNames(start, end) { return (start || "").trim() + " " + (end || "").trim(); }
function playHover() { playContent(hoverSequence); }
function playArt() { playContent(artSequence); }
function playContent(newSequence) {
if ( sequence == newSequence ) {
selector.schedule.hide();
return;
}
roller.classList.add("art-transition");
var transitionMillis = 1000;
setTimeout( () => { roller.classList.remove("art-transition"); }, transitionMillis );
var transitionDelayMillis = 10;
setTimeout( () => {
var index = removeCssClassName(roller, sequence);
if ( index > -1 ) {
roller.className = joinCssClassNames(roller.className, newSequence[index % newSequence.length]);
}
sequence = newSequence;
sparker.orbitStepsAre(sequence.length);
selector.schedule.hide();
}, transitionDelayMillis );
}
var roll = (function() {
var timedRollId;
var clearTimedRoll = () => { timedRollId = null; };
var shedWhirl = function() {
var div = document.createElement("div");
div.className = "shed whirl";
div.addEventListener("click", function(event) { event.stopPropagation(); });
element.of(".whirl").appendChild(div);
return div;
};
var wave = () => {
if ( timedRollId || sparker.isPaused() ) { return; }
var rollDelayMillis = 250;
timedRollId = setTimeout(() => {
var shedDiv = shedWhirl();
waver.wave({ update: function() { shedDiv.remove(); } });
clearTimedRoll();
}, rollDelayMillis );
};
var balance = () => {
if (timedRollId) {
clearTimeout(timedRollId);
clearTimedRoll();
}
sparker.pauseOrShift();
};
var reveal = (event) => { selector.schedule.reveal(event); };
var millisBetweenShifts = 500;
var balancer = threshold.after(2).taps().inMillis(millisBetweenShifts/2).or(balance);
var shift = threshold.after(millisBetweenShifts).millis().or(() => {});
var animator = () => { balancer(() => { shift(() => { wave(); } ); }); };
return { animation: (event) => { blur(); animator(); }
, clear: { timedRoll: () => { clearTimedRoll(); } }
};
})();
function rollAnimation(event) { roll.animation(event); }
var selector = (function(picker, gauge) {
var scheduleds = (function() {
var revealId, hideId;
return { reveal: { id: (scheduleId) => { if ( scheduleId ) { revealId = scheduleId; } return revealId; }
, clear: () => { revealId = null; }
}
, hide: { id: (scheduleId) => { if ( scheduleId) { hideId = scheduleId; } return hideId; }
, clear: () => { hideId = null; }
} };
})();
var hideContentSelector = function() { if ( picker.element() ) { picker.element().classList.remove("visible"); } };
var captureClick = function(event) { event.stopPropagation(); window.removeEventListener("click", captureClick, true); };
return { schedule: { reveal: (event) => {
if ( picker.element().classList.contains(".visible") || event.target != gauge.element() ) { return; }
var mouseHoldDownMillis = 2000;
var visibilityMillis = 7000;
var timedRevealId = setTimeout( () => {
window.addEventListener("click", captureClick, true);
picker.element().classList.add("visible");
scheduleds.reveal.clear();
var timedHideId = setTimeout( () => {
hideContentSelector();
scheduleds.hide.clear();
}
, visibilityMillis );
scheduleds.hide.id(timedHideId);
}, mouseHoldDownMillis );
scheduleds.reveal.id(timedRevealId);
}
, hide: () => { hideContentSelector(); }
}
, cancel: { reveal: (event) => {
if ( !scheduleds.reveal.id() || event.target != gauge.element() ) { return; }
clearTimeout(scheduleds.reveal.id());
scheduleds.reveal.clear();
}
, hide: () => {
if ( scheduleds.hide.id() ) {
clearTimeout(scheduleds.hide.id());
scheduleds.hide.clear();
}
} }
};
})(buffered.source(() => element.of(".art-picker")), buffered.source(() => element.of(".whirl")));
function scheduleRevealContentSelector(event) { selector.schedule.reveal(event); }
function cancelRevealContentSelector(event) { selector.cancel.reveal(event); }
var observableValue = function(value) {
return ( function() {
var observed = value;
var get = function () { return observed; };
var set = function (newValue) { observed = newValue; };
var withdraw = function (value) {
var once = function(values) {
var index = values.indexOf(value);
if ( index > -1 ) {
values.splice(index, 1);
return true;
}
return false;
}
var remover = once;
return { from: function(values) {
return remover(values);
}
, entierly: function() {
remover = function(values) {
var index = values.indexOf(value);
if ( index == -1 ) { return false; }
for ( var i = 0; i < values.length; i++ ) {
if ( values[i] === value ) {
values.splice(i, 1);
}
}
return true;
};
return this;
}
, once: function() {
remover = once;
return this;
}};
};
return { value: get
, observers: []
, oneTimeObservers: []
, is: function(value) {
var priorValue = get();
set(value);
this.updateObservers(priorValue, value);
}
, updateObservers: function(priorValue, value) {
var context = this;
setTimeout( () => {
context.observers.forEach((observer) => { observer.update.call(observer, priorValue, value); });
var oneTimeObservers = context.oneTimeObservers;
for ( var i = 0; i < oneTimeObservers.length; i++ ) {
var observer = oneTimeObservers.pop();
if ( !observer.update.call(observer, priorValue, value) ) {
context.oneTimeObservers.push(observer);
}
}
}, 1);
}
, observedBy: function(observer) {
this.observers.push(observer);
return true;
}
, oneTimeObservedBy: function(observer) {
this.oneTimeObservers.push(observer);
return true;
}
, withdrawObserver: function(observer) {
return withdraw(observer).from(this.observers);
}
, withdrawOneTimeObserver: function(observer) {
return withdraw(observer).from(this.oneTimeObservers);
}};
})();};
var wobbler = function(orbitStrategies) {
var orbit = orbitStrategies[0] || sameStepOrbitStrategy;
var orbitDirectionOf = function(orbit, firstStep, secondStep) {
var first = firstStep || orbit.startStep() || 0
, second = secondStep || orbit.nextStep(first)
, third = orbit.nextStep(second);
return second == 0 && first > third || third == 0 && first < second || first < second && second < third ? "clock" : "counter clock";
};
return {
activeOrbit: this.observableValue(orbit)
, orbits: orbitStrategies
, activeStep: this.observableValue()
, orbitDirection: orbitDirectionOf(orbit, orbit.startStep() || 0)
, orbitDirectionOf: orbitDirectionOf
, orbitObservedBy: function(observer) {
return this.activeOrbit.observedBy(observer);
}
, withdrawOrbitObserver: function(observer) {
return this.activeOrbit.withdrawObserver(observer);
}
, stepOneTimeObservedBy: function(observer) {
this.stepOneTimeObservers.push(observer);
}
, transitionOrbit: function() {
return ( this.orbitDirection == "clock" ) ? clockOrbitStrategy : counterClockOrbitStrategy;
}
, orbitIs: function(orbit) {
this.activeOrbit.is(orbit);
}
, nextStep: function(activeStep) {
var orbit = this.activeOrbit.value();
var nextStep = orbit.nextStep(activeStep);
this.orbitDirection = this.orbitDirectionOf(orbit, activeStep, nextStep);
this.activeStep.is(nextStep);
return this.activeStep.value();
}
, wave: function(orbitOneTimeObserver) {
var nextOrbit = this.nextOrbit()
, orbit = this.activeOrbit.value()
, activeWaver = this
, oneTimeObserved = (orbit) => { if ( orbitOneTimeObserver ) { orbit.oneTimeObservedBy(orbitOneTimeObserver); } }
, pendingOrbit = function(nextOrbit) { this.activeStep.oneTimeObservedBy({ step: nextOrbit.startStep()
, waver: activeWaver
, orbit: nextOrbit
, update: function(previousStep, step) {
if ( this.step != null && this.step != step ) { return false; }
this.waver.orbitIs(this.orbit);
oneTimeObserved(this.waver.activeOrbit);
return true;
} } ); };
var nextOrbitDirection = this.orbitDirectionOf(nextOrbit);
if ( nextOrbitDirection == this.orbitDirection ) {
this.activeOrbit.is( this.transitionOrbit() );
pendingOrbit.call(this, nextOrbit);
} else {
if ( orbit.endStep() || nextOrbit.startStep() ) {
this.activeStep.oneTimeObservedBy({ step: orbit.endStep()
, waver: activeWaver
, orbit: nextOrbit
, update: function(previousStep, step) {
if ( this.step != null && this.step != step ) { return false; }
var activeOrbit = this.waver.activeOrbit
, startStep = this.orbit.startStep();
if ( startStep && startStep != step ) {
activeOrbit.is( this.waver.transitionOrbit() );
pendingOrbit.call(this.waver, this.orbit);
} else {
activeOrbit.is( this.orbit );
oneTimeObserved(activeOrbit);
}
return true;
} } );
} else {
pendingOrbit.call(this, nextOrbit);
}
}
}
, nextOrbit: function() {
var orbit = sameStepOrbitStrategy;
var orbitCount = this.orbits.length;
for ( var i = 0; i < orbitCount; i++ ) {
if ( this.activeOrbit.value() == this.orbits[i] ) {
orbit = this.orbits[(i + 1) % orbitCount];
break;
}
}
return orbit;
}
};
};
var rotor = function(area, numberOfSteps, waver, stepDurationsMillis) {
var gear = { supply: function(handler) {
var next = observableValue(0);
var activeHandlerIndex = 0;
var neutral = () => { return null; };
var handlers = [neutral];
var handle = { it: function(handlee) { if ( handlee ) { return handlers.push(handlee); } return false; } };
(function(handlee) { handle.it(handlee); })(handler);
return {
handled: { by: function(handler) {
return (handlers.length == 2) ? false : handle.it(handler) && true;
} }
, activeHandler: function() {
return handlers[activeHandlerIndex];
}
, couple: function(next, context, stepDurationMillis) {
var handler = this.activeHandler()
, handlerContext = this;
return handler.call(handlerContext, next, context, stepDurationMillis);
}
, shift: { it: function() { activeHandlerIndex = (activeHandlerIndex + 1) % handlers.length; }
, to: { neutral: function() { activeHandlerIndex = 0; } }
}
, isNeutral: function() { return activeHandlerIndex == 0; }
, stepObservedBy: function(observer) { next.observedBy(observer); }
, withdrawStepObserver: function(observer) { next.withdrawObserver(observer); }
, stepValueIs: function(step) { next.is(step); }
, stepValue: function() { return next.value(); }
};
} };
var visualizer = function(numberOfValues, visual) {
var valueShades = (function(numberOfShades) {
var shades = []
, shadeStep = Math.floor(50 / numberOfShades);
for ( var i = 0; i < numberOfShades; i++ ) {
shades.push("hsl(205, 20%, " + (50 + shadeStep * i) + "%)");
}
return shades;
})(numberOfValues - 1).concat(["transparent"]);
return {
visual: visual
, valueShades: valueShades
, visual: visual
, update: function(previousIndex, activeIndex) {
this.visual.style.backgroundColor = this.valueShades[activeIndex];
}
};
};
var dot = (function() {
var dot = document.createElement("div");
dot.classList.add("dot");
return dot;
})();
var computeOrbitSteps = function(numberOfSteps) {
var steps = []
, radians = ((360 / numberOfSteps) * 2 * Math.PI) / 360
, cos = Math.cos(radians)
, sin = Math.sin(radians)
, radius = Math.round(area.offsetWidth / 2)
, originX = radius
, originY = radius
, margin = radius - Math.round(radius / 2)
, x1 = radius - margin
, y1 = radius - margin;
for ( var i = 0; i < numberOfSteps; i++ ) {
var x2 = (Math.round(x1 * cos - y1 * sin))
, y2 = (Math.round(y1 * cos + x1 * sin))
, x = originX + x2
, y = originY + y2;
steps.push([x, y]);
x1 = x2;
y1 = y2;
}
return steps;
};
var orbitSteps = computeOrbitSteps(numberOfSteps);
var stepDurationsMillisArray = [stepDurationsMillis].reduce((accumulator, element) => accumulator.concat(element), []);
var handler = function(nextStep, context, durationMillis) {
this.stepValueIs(nextStep);
return setTimeout( () => { context.step.call(context, nextStep); }
, durationMillis );
};
var coupler = gear.supply(handler);
var observableStepDurationIndex = this.observableValue();
var visualHint = visualizer(stepDurationsMillisArray.length, area);
observableStepDurationIndex.observedBy(visualHint);
return {
orbiter: dot
, rotorArea: area
, orbitSteps: orbitSteps
, waver: waver
, inMotion: this.observableValue()
, timedStepsId: []
, stepDurationsMillisArray: stepDurationsMillisArray
, coupler: coupler
, stepDurationIndex: observableStepDurationIndex
, visualHint: visualHint
, stepDurationIndexObservedBy: function(observer) {
this.stepDurationIndex.observedBy(observer);
}
, stepObservedBy: function(observer) {
this.coupler.stepObservedBy(observer);
}
, computeOrbitSteps: computeOrbitSteps
, on: function() {
this.stepDurationIndex.is(0);
this.resume();
this.rotorArea.appendChild(this.orbiter);
}
, orbitStepsAre: function(numberOfSteps) {
this.off();
while (!this.isPaused()) {
this.pauseOrShift();
}
this.orbitSteps = this.computeOrbitSteps(numberOfSteps);
this.coupler.stepValueIs(this.coupler.stepValue() % numberOfSteps);
this.resume();
}
, off: function() {
this.pause();
for ( var i = 0; i < this.timedStepsId.length; i++ ) {
clearTimeout(this.timedStepsId[i]);
}
}
, resume: function() {
this.coupler.shift.it();
this.step(this.coupler.stepValue());
this.inMotion.is(true);
}
, pause: function() {
this.coupler.shift.to.neutral();
this.inMotion.is(false);
}
, isPaused: function() {
return this.coupler.isNeutral();
}
, pauseOrShift: function() {
if ( this.isPaused() ) {
this.resume();
return;
}
var stepMillisLength = this.stepDurationsMillisArray.length;
var durationIndex = 0;
if ( this.stepDurationIndex.value() == stepMillisLength - 1 ) {
this.pause();
} else {
durationIndex = (this.stepDurationIndex.value() + 1) % stepMillisLength;
}
this.stepDurationIndex.is(durationIndex);
}
, motionObservedBy: function(observer) {
this.inMotion.observedBy(observer);
}
, withdrawMotionObserver: function(observer) {
return this.inMotion.withdrawObserver(observer);
}
, step: function(step) {
this.timedStepsId.pop();
this.orbiter.style.left = this.orbitSteps[step][0];
this.orbiter.style.top = this.orbitSteps[step][1];
var context = this
, nextStep = this.waver.nextStep(step)
, timedStepId = this.coupler.couple(nextStep, context, this.stepDurationsMillisArray[this.stepDurationIndex.value()]);
if ( timedStepId ) { this.timedStepsId.push(timedStepId); }
}
};
};
var rollOn = (function() {
var loadedImagesCounter = 0;
return () => {
loadedImagesCounter++;
if ( loadedImagesCounter < sequence.length ) { return; }
var cssClasses = element.of(".roller").classList
, cssClassName = sparker.waver.activeOrbit.value().cssClassName;
if ( cssClassName.length > 0 ) { cssClasses.add(cssClassName); }
cssClasses.remove("transparent");
sparker.stepObservedBy( { update: function(previousStep, step) {
removeCssClassName(roller, sequence);
roller.className = joinCssClassNames(roller.className, sequence[step % sequence.length]);
} });
setTimeout( () => {
element.of("body").classList.remove("loading");
sparker.orbitStepsAre(sequence.length);
}, 8000 );
};
})();
</script>
</head>
<body class="loading">
<div class="roller centered sized transparent">
<div class="alternative-text">...</div>
<div class="minute centered zero quarter"><div class="symbol">00</div><div class="stage"><div class="symbol">30</div></div></div>
<div class="minute centered one"><div class="symbol">01</div><div class="stage"><div class="symbol">31</div></div></div>
<div class="minute centered two"><div class="symbol">02</div><div class="stage"><div class="symbol">32</div></div></div>
<div class="minute centered three"><div class="symbol">03</div><div class="stage"><div class="symbol">33</div></div></div>
<div class="minute centered four"><div class="symbol">04</div><div class="stage"><div class="symbol">34</div></div></div>
<div class="minute centered five fifth top"><div class="symbol">05</div><div class="stage"><div class="symbol">35</div></div></div>
<div class="minute centered six"><div class="symbol">06</div><div class="stage"><div class="symbol">36</div></div></div>
<div class="minute centered seven"><div class="symbol">07</div><div class="stage"><div class="symbol">37</div></div></div>
<div class="minute centered eight"><div class="symbol">08</div><div class="stage"><div class="symbol">38</div></div></div>
<div class="minute centered nine"><div class="symbol">09</div><div class="stage"><div class="symbol">39</div></div></div>
<div class="minute centered ten fifth"><div class="symbol">10</div><div class="stage"><div class="symbol">40</div></div></div>
<div class="minute centered eleven"><div class="symbol">11</div><div class="stage"><div class="symbol">41</div></div></div>
<div class="minute centered twelve"><div class="symbol">12</div><div class="stage"><div class="symbol">42</div></div></div>
<div class="minute centered thirteen"><div class="symbol">13</div><div class="stage"><div class="symbol">43</div></div></div>
<div class="minute centered fourteen"><div class="symbol">14</div><div class="stage"><div class="symbol">44</div></div></div>
<div class="minute centered fifteen quarter"><div class="symbol">15</div><div class="stage"><div class="symbol">45</div></div></div>
<div class="minute centered sixteen"><div class="symbol">16</div><div class="stage"><div class="symbol">46</div></div></div>
<div class="minute centered seventeen"><div class="symbol">17</div><div class="stage"><div class="symbol">47</div></div></div>
<div class="minute centered eighteen"><div class="symbol">18</div><div class="stage"><div class="symbol">48</div></div></div>
<div class="minute centered nineteen"><div class="symbol">19</div><div class="stage"><div class="symbol">49</div></div></div>
<div class="minute centered twenty fifth"><div class="symbol">20</div><div class="stage"><div class="symbol">50</div></div></div>
<div class="minute centered twentyone"><div class="symbol">21</div><div class="stage"><div class="symbol">51</div></div></div>
<div class="minute centered twentytwo"><div class="symbol">22</div><div class="stage"><div class="symbol">52</div></div></div>
<div class="minute centered twentythree"><div class="symbol">23</div><div class="stage"><div class="symbol">53</div></div></div>
<div class="minute centered twentyfour"><div class="symbol">24</div><div class="stage"><div class="symbol">54</div></div></div>
<div class="minute centered twentyfive fifth"><div class="symbol">25</div><div class="stage"><div class="symbol">55</div></div></div>
<div class="minute centered twentysix"><div class="symbol">26</div><div class="stage"><div class="symbol">56</div></div></div>
<div class="minute centered twentyseven"><div class="symbol">27</div><div class="stage"><div class="symbol">57</div></div></div>
<div class="minute centered twentyeight"><div class="symbol">28</div><div class="stage"><div class="symbol">58</div></div></div>
<div class="minute centered twentynine"><div class="symbol">29</div><div class="stage"><div class="symbol">59</div></div></div>
</div>
<div class="bottom-lane" onmousedown="scheduleRevealContentSelector(event)" onmouseup="cancelRevealContentSelector(event)">
<div class="art-picker" onclick="blur()">
<div class="cluster cluster-art" onclick="playArt()">
<div class="alternative-text">. . . .</div>
<div class="key-frame"><div class="key-wrap centered">
<div class="key">1</div><div class="key">2</div><div class="key">3</div><div class="key">4</div><div class="key">5</div>
<div class="key">6</div><div class="key">7</div><div class="key">8</div><div class="key">9</div><div class="key">0</div>
</div></div>
</div>
<div class="cluster cluster-hover" onclick="playHover()">
<div class="alternative-text">. . . . . . . .</div>
<div class="key-frame"><div class="key-wrap centered"><div class="key"></div></div></div>
</div>
</div>
<div class="whirl centered" onclick="rollAnimation(event)"></div>
</div>
</body>
<script type="text/javascript">
var emptyFunction = () => { return null; }
, sameStepOrbitStrategy = { type: "same step"
, cssClassName: ""
, startStep: emptyFunction
, endStep: emptyFunction
, nextStep: function(step) { return step; }
}
, clockOrbitStrategy = { type: "clock"
, cssClassName: "clock"
, startStep: emptyFunction
, endStep: emptyFunction
, nextStep: function(step) { return (step + 1) % sequence.length; }
}
, counterClockOrbitStrategy = { type: "counter clock"
, cssClassName: "counter-clock"
, startStep: emptyFunction
, endStep: emptyFunction
, nextStep: function(step) {
if ( step == 0 ) { return sequence.length - 1; } else { return step - 1; }
} }
, alternateOrbitStrategy = { type: "alternate"
, cssClassName: "alternate"
, startStep: () => { return 1; }
, endStep: () => { return 7 % sequence.length; }
, stepFunction: (step) => { return step + 1; }
, nextStep: function(step) {
var adaptedStep = step % sequence.length;
if ( adaptedStep == this.endStep() ) { this.stepFunction = (step) => { return step - 1; }; }
if ( adaptedStep == this.startStep() ) { this.stepFunction = (step) => { return step + 1; }; }
return this.stepFunction(adaptedStep);
} }
, orbitStrategies = [clockOrbitStrategy, counterClockOrbitStrategy, alternateOrbitStrategy]
, roller = element.of(".roller")
, waver = wobbler(orbitStrategies);
waver.orbitObservedBy( { roller: roller
, update: function(previousOrbit, orbit) {
var cssToRemove = previousOrbit.cssClassName
, cssToAdd = orbit.cssClassName
, className = " " + this.roller.className + " ";
roller.className = className.replace(" " + cssToRemove + " ", " " + cssToAdd + " ").trim();
} });
var sparker = this.rotor(element.of(".whirl"), sequence.length, waver, [1000, 500, 250]);
(function() {
sparker.stepDurationIndexObservedBy( { speedMarkerCssClasses: ["slow", "moderate", "fast"]
, update: function(activeIndex, index) {
removeCssClassName(roller, this.speedMarkerCssClasses);
roller.classList.add(this.speedMarkerCssClasses[index % this.speedMarkerCssClasses.length]);
} });
sparker.motionObservedBy( { update: function(previous, active) {
if ( active ) {
removeCssClassName(roller, ["paused"]);
} else {
roller.classList.add("paused");
}
}});
sparker.on();
})();
(function() {
var delay = 1;
for ( var i = 0; i < hoverSequence.length; i++ ) {
setTimeout(() => { rollOn(); }, delay * i);
}
})();
</script>
</html>
<>
icon for a JS Snippet, so we cannot see it in action on this site, and you offered no links to a demo site. \$\endgroup\$class
notation withmethods
, instead of chaining functions in an object separated by a,
? Another potential issue is that functionality and graphic representation are mixed, so now I can't use therotor
to drive any different kind of animation than the one you hardcoded. \$\endgroup\$