Skip to content

Instantly share code, notes, and snippets.

@felds
Last active September 27, 2016 16:12
Show Gist options
  • Select an option

  • Save felds/514140add896803f4c1ac6f35a249cb3 to your computer and use it in GitHub Desktop.

Select an option

Save felds/514140add896803f4c1ac6f35a249cb3 to your computer and use it in GitHub Desktop.
JQuery Scrubber
<div class="slideshow" id="slideshow">
<div class="slideshow__bgs">
<div style="background-image: url(https://unsplash.it/800/600?image=4)"></div>
<div style="background-image: url(https://unsplash.it/800/600?image=3)"></div>
<div style="background-image: url(https://unsplash.it/800/600?image=2)"></div>
<div style="background-image: url(https://unsplash.it/800/600?image=1)"></div>
</div>
<div class="slideshow__fg">
<div class="scrubber" data-scrubber-steps="4" id="scr-slideshow"></div>
</div>
</div>
<h1>Examples</h1>
<h3>2 steps with custom elements</h3>
<div class="scrubber" id="scrubber-1" data-scrubber-steps="2">
<div class="scrubber__track">
<b class="scrubber__handle bleu"></b>
</div>
</div>
<h3>3 steps</h3>
<div class="scrubber" id="scrubber-2" data-scrubber-steps="3"></div>
<h3>1 step (fluid)<h3>
<div class="scrubber" id="scrubber-3" data-scrubber-steps="1"></div>
<h3>4 steps</h3>
<div class="scrubber" id="scrubber-4" data-scrubber-steps="4"></div>
<h3>5 steps</h3>
<div class="scrubber" id="scrubber-5" data-scrubber-steps="5"></div>
<h3>C/ Saveiro</h3>
<div class="scrubber" id="scr-saveiro" data-scrubber-steps="4"></div>
<img src="http://novasaveiro.vw.com.br/img/large/05_360_02_Saveiro_Trend_Volkswagen_00001.png" alt="Saveiro" id="img-saveiro" />
<h1>TODOs</h1>
<ul>
<li>Add click to scroll</li>
<li>&check; Get steps from data</li>
<li>&check; WTF IS X?!</li>
<li>&check; use percents</li>
<li>&check; enforce boudaries</li>
<li>&check; move events to the scrubber (instead of triggering from the handle)</li>
</ul>
console.clear()
// Loading bar for console output
const loadingBar = (relative, width = 50) => {
const loaded = Math.floor(width * relative)
return '█'.repeat(loaded) + '▒'.repeat(width - loaded)
}
// mix colors for testing relative stuff
const mid = (a, b, amp) =>
a + (b - a) * amp
const mixHex = (a, b, amp) =>
('00' + Math.floor(mid(parseInt(a, 16), parseInt(b, 16), amp)).toString(16)).substr(-2)
const cleanupHexColor = (color) => color.match(/[0-9a-f]{2}/ig)
const gradient = (from, to) => (amp) => {
const fromVals = cleanupHexColor(from)
const toVals = cleanupHexColor(to)
return '#' +
mixHex(fromVals[0], toVals[0], amp) +
mixHex(fromVals[1], toVals[1], amp) +
mixHex(fromVals[2], toVals[2], amp)
}
const grad1 = gradient('#ffd700', '#ed143d')
expect(grad1(0)).toBe('#ffd700')
expect(grad1(.5)).toBe('#f6751e')
expect(grad1(.1234)).toBe('#fcbe07')
expect(grad1(1)).toBe('#ed143d')
const gr1 = gradient('#ffffff', '#ffff00')
console.log(gr1(.90))
// initialize stuff
jQuery(($) => {
let x = $('.scrubber')
.scrubber({ steps: 0 })
;
$('#scrubber-3').on('change', (e, v) => console.log(loadingBar(v.relative)))
const grad = gradient('#397ee3', '#dc143c')
const scr2 = $('#scrubber-2')
scr2.on('change', (e, v) => scr2.find('.scrubber__handle').css({ backgroundColor: grad(v.relative) }))
const grad2 = gradient('#F57D7D', '#7DD1F5')
const scr3 = $('#scrubber-3')
scr3
.on('change', (e, v) => {
scr3.css({ backgroundColor: grad2(v.relative) })
})
.trigger('update')
const scr5 = $('#scrubber-5')
scr5.on('change', (e, v) => {
let steps = [ 'crimson', '#808000', '#00551C', 'blue', 'purple' ]
scr5.css({ backgroundColor: steps[v.step] })
}).trigger('update')
// saveiro
const scrSaveiro = $('#scr-saveiro')
const imgSaveiro = $('#img-saveiro')
const getSavImage = (n = 1) => {
let id = ('0000000' + n).substr(-5)
return `http://novasaveiro.vw.com.br/img/large/05_360_02_Saveiro_Trend_Volkswagen_${id}.png`
}
scrSaveiro.on('change', (e, v) => {
imgSaveiro.prop({ src: getSavImage(Math.floor(v.relative * 59) + 1) })
}).trigger('update')
for (let i = 1; i <= 60; i++) {
let img = new Image()
img.src = getSavImage(i)
}
scr5.on('change', (e, v) => {
imgSaveiro.css({ filter: `hue-rotate(${Math.floor(v.relative*288)}deg)` })
}).trigger('update')
const slideshow = $('#slideshow')
const slides = slideshow.find('.slideshow__bgs div')
slideshow.find('#scr-slideshow').on('change', (e, v) => {
// let slice = v.relative * slides.length
let rel = v.relative
let len = slides.length
console.clear()
slides.each(i => {
let opacity = 1 + ((rel * len) - (len - i) + (i / (len - 1)))
console.log({opacity})
slides.eq(i).css({ opacity })
})
let factor = 4
let t4 = v.relative * 4
//console.log(
// slides.length,
// t4.toFixed(3),
// //v.relative.toFixed(3),
// //(1-v.relative).toFixed(3),
// //(2-v.relative).toFixed(3),
// (t4-3 + (0/(factor-1))).toFixed(3),
// (t4-2 + (1/(factor-1))).toFixed(3),
// (t4-1 + (2/(factor-1))).toFixed(3),
// (t4-0 + (3/(factor-1))).toFixed(3),
//)
// const sliceSize = 1 / 3 // (slides.length - 1)
// console.log(1 - sliceSize)
// slides.eq(1).css({
// opacity: (sliceSize * 1) - v.relative,
// })
}).trigger('update')
})
// plugin
$.fn.scrubber = function (receivedOptions = {}) {
const defaultOptions = {
steps: 1,
animationSpeed: 'fast',
animationEasing: 'swing',
trackClass: 'scrubber__track',
handleClass: 'scrubber__handle',
}
const options = $.extend(defaultOptions, receivedOptions)
const $root = $(':root')
this.each(i => {
const $scrubber = this.eq(i)
let steps = $scrubber.data('scrubber-steps') || options.steps
let offsetFromHandle = 0
// support elements
const $track = $scrubber.find('.' + options.trackClass).length
? $scrubber.find('.' + options.trackClass).eq(0)
: ($(`<div class="${options.trackClass}" />`).appendTo($scrubber))
const $handle = $track.find('.' + options.handleClass).length
? $track.find('.' + options.handleClass).eq(0)
: ($(`<b class="${options.handleClass}" />`).appendTo($track))
$handle.css({
cursor: 'pointer',
position: 'absolute',
left: 0,
top: 0,
})
$track.css({
position: 'relative',
marginRight: $handle.outerWidth(),
})
$handle.on('mousedown', e => {
offsetFromHandle = e.offsetX
startDragging()
e.preventDefault()
})
const trackMovement = (e) => {
// track the movement of the mouse on :root
const offset = e.screenX
const offsetFromParent = $track.offset().left
const parentWidth = $track.width()
let absolute = offset - offsetFromParent - offsetFromHandle
if (absolute < 0)
absolute = 0
if (absolute > parentWidth)
absolute = parentWidth
let relative = absolute / parentWidth
$handle.css({ left: `${relative * 100}%` })
updateValue()
}
const startDragging = () => {
$scrubber.trigger('startdragging')
// attach trackMovement to root
$root
.on('mousemove', trackMovement)
.on('mouseup', stopDragging)
}
const stopDragging = () => {
const stop = getPosition().stop
// animate to the center of the step
$handle.animate(
{ left: `${stop * 100}%` }, {
easing: options.animationEasing,
duration: options.animationSpeed,
progress: updateValue,
}
)
// trigger event
// @TODO add position to the event
$scrubber.trigger('stopdragging')
// detach trackMOvement from root
$root
.off('mousemove', trackMovement)
.off('mouseup', stopDragging)
}
const updateValue = () => {
// get the current values and trigger a 'change' event
$scrubber.trigger('change', [ getPosition() ])
}
const getPosition = () => {
const absolute = $handle.position().left
const relative = absolute / $track.width()
const sliceSize = 1 / (steps * 2 - 2)
const step = Math.floor((relative + sliceSize) / (sliceSize * 2))
const stop = sliceSize * step * 2
return { absolute, relative, stop, step, sliceSize }
}
$scrubber.trigger('init', [ getPosition() ])
$scrubber.on('update', updateValue)
})
return this
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/expect/1.20.2/expect.min.js"></script>
$color: mix(#333, dodgerblue);
:root {
background-color: whitesmoke;
box-sizing: border-box;
* { box-sizing: inherit }
}
body {
margin: 2rem;
}
.scrubber {
height: 2rem;
outline: 1px solid $color;
margin: 2rem;
background: mix($color, white)
}
.scrubber__track {
height: 100%;
}
.scrubber__handle {
height: 100%;
background-color: $color;
width: 2rem;
}
#scrubber-2 { width: 80% }
#scrubber-1 .scrubber__handle { width: 4rem; border-radius: 1rem; }
.bleu {
background-color: dodgerblue;
}
#img-saveiro {
width: 100%;
height: auto;
}
.slideshow {
width: 800px;
height: 600px;
position: relative;
margin: 2rem auto;
}
.slideshow__bgs {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
outline: 1px solid crimson;
> div {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
z-index: 0;
}
}
.slideshow__fg {
position: absolute;
bottom: 2rem;
width: 100%;
z-index: 9;
padding: 0 4rem;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment