Skip to content

Instantly share code, notes, and snippets.

@JoshBarr
Created March 12, 2015 04:44
Show Gist options
  • Save JoshBarr/a33232af242bc5a2f5c3 to your computer and use it in GitHub Desktop.
Save JoshBarr/a33232af242bc5a2f5c3 to your computer and use it in GitHub Desktop.
Range slider
<!DOCTYPE html>
<html>
<head>
<title>Slider test</title>
<style type="text/css">
body {
font-family: pitch, courier;
margin: 4em;
}
.slider {
height: 100px;
background: #444;
margin-left: 1em;
margin-right: 1em;
}
.handle {
height: 100%;
cursor: pointer;
width: 0;
position: relative;
}
.actual-handle {
width: 2em;
height: 100%;
margin-left: -1em;
background: #999;
}
.inactive {
display: none;
}
.label {
position: absolute;
bottom: 115%;
width: 3em;
background: #eee;
margin-left: -1.5em;
text-align: center;
padding: .25em .5em;
}
.label:after {
border: solid .5em transparent;
border-top-color: #eee;
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -.5em;
}
</style>
</head>
<body>
<div class="slider inactive">
<div class="handle">
<div class="actual-handle">
</div>
<div class="label inactive">
200px
</div>
</div>
</div>
<script type="text/javascript">
module = {};
</script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js"></script>
<script type="text/javascript" src="slider.js"></script>
<script type="text/javascript">
function init() {
var el = document.querySelector('.slider');
var label = document.querySelector('.label');
var minPointSize = 20;
var maxPointSize = 300;
var range = {
min: minPointSize,
max: maxPointSize
};
function getPointSize(val) {
var r = range.max - range.min;
var n = val * r;
return Math.floor(range.min + n);
}
var slider = new UISliderComponent({
el: el,
handle: document.querySelector('.handle')
});
slider.setValue(.5, true);
el.classList.remove("inactive");
slider.onStart = function(e) {
console.log("start", e);
console.log(getPointSize(e));
label.textContent = getPointSize(e) + "px";
label.classList.remove('inactive');
}
slider.onEnd = function(e) {
console.log("end", e);
console.log(getPointSize(e));
label.classList.add('inactive');
}
slider.onMove = function(e) {
console.log("move", e);
console.log(getPointSize(e));
label.textContent = getPointSize(e) + "px";
}
}
document.addEventListener("DOMContentLoaded", function(e) {
init();
});
</script>
</body>
</html>
function Point(x,y) {
this.x = x;
this.y = y;
}
Point.prototype.get = function get() {
return {
x: this.x,
y: this.y
}
}
Point.prototype.set = function set(x, y) {
this.x = x;
this.y = y;
return this.get();
}
/**
* A simple range slider, sans jquery.
*
* @param {Object} options A hash of properties to be applied to the object
*/
function UISliderComponent(options) {
// Set instance defaults in constructor to avoid accidental
// static variables :)
var opt, defaults = {
el: null,
handle: null,
track: null,
responsive: true,
value: 0,
useRequestAnimationFrame: true,
min: 0,
max: 1,
steps: null,
boundEvents: ["mousedown","touchstart", "selectstart"]
};
for (opt in defaults) {
this[opt] = defaults[opt];
}
for (opt in options) {
this[opt] = options[opt];
}
if (!this.el || !this.handle) {
console.log("Check you've got a handle and el set");
return;
}
this.throttledLayoutUpdate = _.debounce( this.calculateLayout, 500 );
this.isTouch = window.DocumentTouch && document instanceof DocumentTouch;
if (this.responsive) {
window.addEventListener("resize", this, false);
}
this.document = document.documentElement;
// Bind returns a new function ref
this.handleDown = this.handleDown.bind(this);
this.handleEnd = this.handleEnd.bind(this);
this.handleMove = this.handleMove.bind(this);
this.downEvent = "mousedown";
this.moveEvent = "mousemove";
this.endEvent = "mouseup";
if (this.isTouch) {
this.downEvent = "touchstart";
this.moveEvent = "touchmove";
this.endEvent = "touchend";
}
// Handle IE's select start
this.el.addEventListener("selectstart", this, false);
this.el.addEventListener("mousedown", this, false);
this.el.addEventListener("touchstart", this, false);
this.initialize();
};
UISliderComponent.prototype = {
initialize: function() {
this.position = 0;
this.pointer = new Point(0, 0);
this.calculateLayout();
},
handleEvent: function(e) {
switch(e.type) {
case "resize":
// Only catch horizontal events
var iw = window.innerWidth;
if (!this.innerWidth && this.innerWidth !== iw) {
this.throttledLayoutUpdate();
this.innerWidth = iw;
}
break;
case "touchstart":
this.handleDown(e);
break;
case "touchcancel":
this.handleEnd(e);
break;
case "mousedown":
this.handleDown(e);
break;
case "selectstart":
e.preventDefault();
break;
}
},
trigger: function(methodName) {
if (this[methodName] && typeof this[methodName] === "function") {
this[methodName](this.value, this.position);
}
},
handleDown: function(e) {
// e.preventDefault();
this.document.addEventListener("mousemove", this.handleMove, false);
this.document.addEventListener("mouseup", this.handleEnd, false);
if (e.touches) {
this.document.addEventListener("touchmove", this.handleMove, false);
this.document.addEventListener("touchend", this.handleEnd, false);
}
// Especially for responsive, get the layout/bounds when the drag
// is started
this.calculateLayout();
var pointer = this.setPointerCoords(e);
this.isDirty = true;
this.trigger("onStart");
// Don't set the position when the handle is clicked.
// It should already be set by calculateLayout.
if (e.target === this.handle) {
this.trigger("onHandle");
return;
}
this.setPosition(pointer.x);
},
handleMove: function(e) {
e.preventDefault();
var pointer = this.setPointerCoords(e);
this.setPosition(pointer.x);
this.trigger("onMove");
},
handleEnd: function(e) {
// e.preventDefault();
this.document.removeEventListener("mousemove", this.handleMove, false);
this.document.removeEventListener("mouseup", this.handleEnd, false);
this.document.removeEventListener("touchmove", this.handleMove, false);
this.document.removeEventListener("touchend", this.handleEnd, false);
this.trigger("onEnd");
},
setPosition: function(pos){
var value, left;
var localX = pos - this.elementLeft;
var percentage = (localX / this.trackWidth);
if (percentage < 0) { percentage = 0; }
if (percentage > 1) { percentage = 1; }
this.setValue(percentage);
this.position = percentage;
if (this.onSlide && typeof this.onSlide === 'function') {
this.onSlide(left, value);
}
},
setPointerCoords: function(e) {
var x, y;
if (e.touches) {
x = e.touches[0].clientX;
y = e.touches[0].clientY;
} else if (e.currentPoint) {
x = e.currentPoint.x;
y = e.currentPoint.y;
} else {
x = (e.pageX || e.clientX);
y = (e.pageY || e.clientY);
}
return this.pointer.set(x, y);
},
setValue: function(num, triggerSet) {
var value;
if (this.steps) {
value = this.getStepValue(num);
} else {
value = this.getValFromFloat(num);
}
this.updateUI(value);
if (value !== this.value || triggerSet) {
this.value = value;
if (this.isDirty) {
this.trigger("onChange");
}
}
return value;
},
setStep: function(stepNumber) {
var stepValue;
if (!this.step) {
return;
}
if (stepNumber > this.steps) {
stepNumber = this.steps;
}
stepValue = this.step * stepNumber;
this.isDirty = true;
this.setValue(stepValue);
},
updateUI: function(num) {
var cssPercentage = (num * 100);
// Update UI
if (this.fill) {
this.fill.style.width = cssPercentage + '%';
}
this.handle.style.left = cssPercentage + '%';
},
getValueAsFloat: function(value) {
var range = this.max - this.min;
var valRatio = value / range;
return valRatio;
},
getValFromFloat: function(num) {
var range = this.max - this.min;
return (num * range) - this.min;
},
getStepValue: function(percentage) {
var rel = (((percentage) * (this.max - this.min)) + this.min);
var value = this.step * Math.ceil(rel / this.step);
return Number((value).toFixed(2));
},
calculateLayout: function() {
var bounds = this.el.getBoundingClientRect();
this.elementLeft = Math.ceil(bounds.left);
this.elementRight = Math.floor(bounds.right);
this.handleWidth = this.handle.offsetWidth;
this.trackWidth = this.el.offsetWidth;
this.maxHandleX = this.trackWidth - this.handleWidth;
this.handleCenter = this.handleWidth / 2;
if (this.steps) {
this.step = 1 / this.steps;
}
this.position = 0;
this.setValue(this.value, true);
},
// Removes all event listeners we've stored references to.
remove: function() {
var events = this.boundEvents;
for (var e in events) {
this.el.removeEventListener(e, this, false);
}
window.removeEventListener("resize", this, false);
this.document.removeEventListener("mousemove", this.handleMove, false);
this.document.removeEventListener("mouseup", this.handleEnd, false);
this.document.removeEventListener("touchmove", this.handleMove, false);
this.document.removeEventListener("touchend", this.handleEnd, false);
return true;
}
};
module.exports = UISliderComponent;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment