Skip to content

Instantly share code, notes, and snippets.

@bbbrrriiiaaannn
Created February 23, 2015 07:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bbbrrriiiaaannn/a84c38d3916a49851909 to your computer and use it in GitHub Desktop.
Save bbbrrriiiaaannn/a84c38d3916a49851909 to your computer and use it in GitHub Desktop.
JS Bin // source http://jsbin.com/yikule
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<style id="jsbin-css">
.scroller {
position: relative;
}
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
white-space: nowrap;
width: 100%;
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
}
li {
display: inline-block;
width: 200px;
height: 200px;
margin: 10px;
background: red;
}
.prev, .next {
position: absolute;
top: 50%;
background: rgba(255, 255, 255, 0.7);
margin-top: -28px;
transition: opacity 0.3s;
}
.prev.hidden, .next.hidden {
opacity: 0;
pointer-event: none;
}
.prev {
left: 0;
padding: 20px 20px 20px 0;
border-radius: 0 30px 30px 0;
}
.next {
right: 0;
padding: 20px 0 20px 20px;
border-radius: 30px 0 0 30px;
}
</style>
</head>
<body>
<div class="scroller">
<a class="prev hidden"><</a>
<a class="next">></a>
<ul>
<li>hi</li>
<li>hi</li>
<li>hi</li>
<li>hi</li>
<li>hi</li>
</ul>
</div>
<script id="jsbin-javascript">
"use strict";
var _prototypeProperties = function (child, staticProps, instanceProps) { if (staticProps) Object.defineProperties(child, staticProps); if (instanceProps) Object.defineProperties(child.prototype, instanceProps); };
var Scroller = (function () {
// set options(options) {
// let defaultOptions = {
// }
// this._options = Object.mixin(defaultOptions, options) // doesn't actually exist >:(
// }
function Scroller(wrapper) {
var _this = this;
var scrollable = arguments[1] === undefined ? wrapper.querySelector("ul") : arguments[1];
var prev = arguments[2] === undefined ? wrapper.querySelector(".prev") : arguments[2];
var next = arguments[3] === undefined ? wrapper.querySelector(".next") : arguments[3];
return (function () {
// this.options = {}
_this.wrapper = wrapper;
_this.ele = scrollable;
_this.prevEle = prev;
_this.nextEle = next;
_this.isMoving = false;
_this.nextEle.addEventListener("click", _this.next.bind(_this));
_this.prevEle.addEventListener("click", _this.prev.bind(_this));
_this.ele.addEventListener("scroll", _this._debounceUtil(_this._toggleControlVisibility.bind(_this)));
window.addEventListener("resize", _this._toggleControlVisibility.bind(_this));
_this._toggleControlVisibility();
})();
}
_prototypeProperties(Scroller, null, {
_debounceUtil: {
value: function _debounceUtil(func) {
var wait = arguments[1] === undefined ? 100 : arguments[1];
// cribbed from http://davidwalsh.name/javascript-debounce-function
var timeout = undefined;
return function () {
var context = this;
var args = arguments;
var later = function () {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
writable: true,
enumerable: true,
configurable: true
},
_toggleControlVisibility: {
value: function _toggleControlVisibility() {
if (this.isMoving) return; // wait a second, geez
// console.log({sl: this.ele.scrollLeft, cw: this.ele.clientWidth, summed: this.ele.scrollLeft+this.ele.clientWidth, sw: this.ele.scrollWidth});
this.prevEle.classList[this.ele.scrollLeft <= 0 ? "add" : "remove"]("hidden");
this.nextEle.classList[this.ele.scrollLeft + this.ele.clientWidth >= this.ele.scrollWidth ? "add" : "remove"]("hidden");
},
writable: true,
enumerable: true,
configurable: true
},
next: {
value: function next() {
this._move(1);
},
writable: true,
enumerable: true,
configurable: true
},
prev: {
value: function prev() {
this._move(-1);
},
writable: true,
enumerable: true,
configurable: true
},
_move: {
value: function _move() {
var vector = arguments[0] === undefined ? 1 : arguments[0];
if (this.isMoving) return;
var _ref = [this.ele.clientWidth, this.ele.scrollLeft, this.ele.scrollWidth];
var width = _ref[0];
var offset = _ref[1];
var scrollWidth = _ref[2];
vector = vector / Math.abs(vector);
var newOffset = offset + vector * width;
newOffset = Math.min(newOffset, scrollWidth - width);
newOffset = Math.max(newOffset, 0);
this._moveItMoveIt(offset, newOffset);
},
writable: true,
enumerable: true,
configurable: true
},
_moveItMoveIt: {
value: function _moveItMoveIt(oldOffset, newOffset) {
var _this = this;
this.isMoving = true;
var startTime = Date.now();
var duration = 300;
var currentOffset = oldOffset;
var animStep = function () {
var currentTime = Date.now();
currentOffset = _this._easeInOutCirc(currentTime - startTime, oldOffset, newOffset - oldOffset, duration);
_this.ele.scrollLeft = currentOffset;
if (currentTime < startTime + duration) {
requestAnimationFrame(animStep);
} else {
_this.isMoving = false;
_this.ele.scrollLeft = newOffset;
_this._toggleControlVisibility();
}
};
requestAnimationFrame(animStep);
},
writable: true,
enumerable: true,
configurable: true
},
_easeInOutCirc: {
value: function _easeInOutCirc(currentIteration, startValue, changeInValue, totalIterations) {
// from http://kirupa.googlecode.com/svn/trunk/easing.js
if ((currentIteration /= totalIterations / 2) < 1) {
return changeInValue / 2 * (1 - Math.sqrt(1 - currentIteration * currentIteration)) + startValue;
}
return changeInValue / 2 * (Math.sqrt(1 - (currentIteration -= 2) * currentIteration) + 1) + startValue;
},
writable: true,
enumerable: true,
configurable: true
}
});
return Scroller;
})();
var yep = new Scroller(document.querySelector(".scroller"));
</script>
<script id="jsbin-source-css" type="text/css">.scroller
position: relative
*
margin: 0
padding: 0
ul
list-style: none
white-space: nowrap
width: 100%
overflow-x: scroll
-webkit-overflow-scrolling: touch
li
display: inline-block
width: 200px
height: 200px
margin: 10px
background: red
.prev, .next
position: absolute
top: 50%
background: rgba(255,255,255, .7)
margin-top: -28px
transition: opacity .3s
&.hidden
opacity: 0
pointer-event: none
.prev
left: 0
padding: 20px 20px 20px 0
border-radius: 0 30px 30px 0
.next
right: 0
padding: 20px 0 20px 20px
border-radius: 30px 0 0 30px</script>
<script id="jsbin-source-javascript" type="text/javascript">"use strict";
class Scroller {
// set options(options) {
// let defaultOptions = {
// }
// this._options = Object.mixin(defaultOptions, options) // doesn't actually exist >:(
// }
constructor(wrapper, scrollable=wrapper.querySelector("ul"), prev=wrapper.querySelector(".prev"), next=wrapper.querySelector(".next")) {
// this.options = {}
this.wrapper = wrapper;
this.ele = scrollable;
this.prevEle = prev;
this.nextEle = next;
this.isMoving = false;
this.nextEle.addEventListener("click", this.next.bind(this));
this.prevEle.addEventListener("click", this.prev.bind(this));
this.ele.addEventListener("scroll", this._debounceUtil(this._toggleControlVisibility.bind(this)));
window.addEventListener("resize", this._toggleControlVisibility.bind(this));
this._toggleControlVisibility();
}
_debounceUtil(func, wait=100) { // cribbed from http://davidwalsh.name/javascript-debounce-function
let timeout;
return function() {
const context = this
const args = arguments;
const later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
_toggleControlVisibility() {
if(this.isMoving) return; // wait a second, geez
// console.log({sl: this.ele.scrollLeft, cw: this.ele.clientWidth, summed: this.ele.scrollLeft+this.ele.clientWidth, sw: this.ele.scrollWidth});
this.prevEle.classList[(this.ele.scrollLeft <= 0) ? "add" : "remove"]("hidden");
this.nextEle.classList[(this.ele.scrollLeft + this.ele.clientWidth >= this.ele.scrollWidth) ? "add" : "remove"]("hidden");
}
next() {
this._move(1);
}
prev() {
this._move(-1);
}
_move(vector=1) {
if(this.isMoving) return;
const [width, offset, scrollWidth] = [this.ele.clientWidth, this.ele.scrollLeft, this.ele.scrollWidth];
vector = vector/Math.abs(vector);
let newOffset = offset + (vector*width);
newOffset = Math.min(newOffset, scrollWidth-width);
newOffset = Math.max(newOffset, 0);
this._moveItMoveIt(offset, newOffset);
}
_moveItMoveIt(oldOffset, newOffset) {
this.isMoving = true;
let startTime = Date.now();
let duration = 300;
let currentOffset = oldOffset;
let animStep = ()=>{
let currentTime = Date.now();
currentOffset = this._easeInOutCirc(currentTime-startTime, oldOffset, newOffset-oldOffset, duration);
this.ele.scrollLeft = currentOffset;
if( currentTime < startTime+duration ) {
requestAnimationFrame(animStep);
} else {
this.isMoving = false;
this.ele.scrollLeft = newOffset;
this._toggleControlVisibility();
}
};
requestAnimationFrame(animStep);
}
_easeInOutCirc(currentIteration, startValue, changeInValue, totalIterations) { // from http://kirupa.googlecode.com/svn/trunk/easing.js
if ((currentIteration /= totalIterations / 2) < 1) {
return changeInValue / 2 * (1 - Math.sqrt(1 - currentIteration * currentIteration)) + startValue;
}
return changeInValue / 2 * (Math.sqrt(1 - (currentIteration -= 2) * currentIteration) + 1) + startValue;
}
}
var yep = new Scroller(document.querySelector(".scroller"));
</script></body>
</html>
.scroller {
position: relative;
}
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
white-space: nowrap;
width: 100%;
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
}
li {
display: inline-block;
width: 200px;
height: 200px;
margin: 10px;
background: red;
}
.prev, .next {
position: absolute;
top: 50%;
background: rgba(255, 255, 255, 0.7);
margin-top: -28px;
transition: opacity 0.3s;
}
.prev.hidden, .next.hidden {
opacity: 0;
pointer-event: none;
}
.prev {
left: 0;
padding: 20px 20px 20px 0;
border-radius: 0 30px 30px 0;
}
.next {
right: 0;
padding: 20px 0 20px 20px;
border-radius: 30px 0 0 30px;
}
"use strict";
var _prototypeProperties = function (child, staticProps, instanceProps) { if (staticProps) Object.defineProperties(child, staticProps); if (instanceProps) Object.defineProperties(child.prototype, instanceProps); };
var Scroller = (function () {
// set options(options) {
// let defaultOptions = {
// }
// this._options = Object.mixin(defaultOptions, options) // doesn't actually exist >:(
// }
function Scroller(wrapper) {
var _this = this;
var scrollable = arguments[1] === undefined ? wrapper.querySelector("ul") : arguments[1];
var prev = arguments[2] === undefined ? wrapper.querySelector(".prev") : arguments[2];
var next = arguments[3] === undefined ? wrapper.querySelector(".next") : arguments[3];
return (function () {
// this.options = {}
_this.wrapper = wrapper;
_this.ele = scrollable;
_this.prevEle = prev;
_this.nextEle = next;
_this.isMoving = false;
_this.nextEle.addEventListener("click", _this.next.bind(_this));
_this.prevEle.addEventListener("click", _this.prev.bind(_this));
_this.ele.addEventListener("scroll", _this._debounceUtil(_this._toggleControlVisibility.bind(_this)));
window.addEventListener("resize", _this._toggleControlVisibility.bind(_this));
_this._toggleControlVisibility();
})();
}
_prototypeProperties(Scroller, null, {
_debounceUtil: {
value: function _debounceUtil(func) {
var wait = arguments[1] === undefined ? 100 : arguments[1];
// cribbed from http://davidwalsh.name/javascript-debounce-function
var timeout = undefined;
return function () {
var context = this;
var args = arguments;
var later = function () {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
writable: true,
enumerable: true,
configurable: true
},
_toggleControlVisibility: {
value: function _toggleControlVisibility() {
if (this.isMoving) return; // wait a second, geez
// console.log({sl: this.ele.scrollLeft, cw: this.ele.clientWidth, summed: this.ele.scrollLeft+this.ele.clientWidth, sw: this.ele.scrollWidth});
this.prevEle.classList[this.ele.scrollLeft <= 0 ? "add" : "remove"]("hidden");
this.nextEle.classList[this.ele.scrollLeft + this.ele.clientWidth >= this.ele.scrollWidth ? "add" : "remove"]("hidden");
},
writable: true,
enumerable: true,
configurable: true
},
next: {
value: function next() {
this._move(1);
},
writable: true,
enumerable: true,
configurable: true
},
prev: {
value: function prev() {
this._move(-1);
},
writable: true,
enumerable: true,
configurable: true
},
_move: {
value: function _move() {
var vector = arguments[0] === undefined ? 1 : arguments[0];
if (this.isMoving) return;
var _ref = [this.ele.clientWidth, this.ele.scrollLeft, this.ele.scrollWidth];
var width = _ref[0];
var offset = _ref[1];
var scrollWidth = _ref[2];
vector = vector / Math.abs(vector);
var newOffset = offset + vector * width;
newOffset = Math.min(newOffset, scrollWidth - width);
newOffset = Math.max(newOffset, 0);
this._moveItMoveIt(offset, newOffset);
},
writable: true,
enumerable: true,
configurable: true
},
_moveItMoveIt: {
value: function _moveItMoveIt(oldOffset, newOffset) {
var _this = this;
this.isMoving = true;
var startTime = Date.now();
var duration = 300;
var currentOffset = oldOffset;
var animStep = function () {
var currentTime = Date.now();
currentOffset = _this._easeInOutCirc(currentTime - startTime, oldOffset, newOffset - oldOffset, duration);
_this.ele.scrollLeft = currentOffset;
if (currentTime < startTime + duration) {
requestAnimationFrame(animStep);
} else {
_this.isMoving = false;
_this.ele.scrollLeft = newOffset;
_this._toggleControlVisibility();
}
};
requestAnimationFrame(animStep);
},
writable: true,
enumerable: true,
configurable: true
},
_easeInOutCirc: {
value: function _easeInOutCirc(currentIteration, startValue, changeInValue, totalIterations) {
// from http://kirupa.googlecode.com/svn/trunk/easing.js
if ((currentIteration /= totalIterations / 2) < 1) {
return changeInValue / 2 * (1 - Math.sqrt(1 - currentIteration * currentIteration)) + startValue;
}
return changeInValue / 2 * (Math.sqrt(1 - (currentIteration -= 2) * currentIteration) + 1) + startValue;
},
writable: true,
enumerable: true,
configurable: true
}
});
return Scroller;
})();
var yep = new Scroller(document.querySelector(".scroller"));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment