Skip to content

Instantly share code, notes, and snippets.

@Aqours
Created June 4, 2017 01:23
Show Gist options
  • Save Aqours/3af6a5663f1ac290b1d5e23f5f5e91a1 to your computer and use it in GitHub Desktop.
Save Aqours/3af6a5663f1ac290b1d5e23f5f5e91a1 to your computer and use it in GitHub Desktop.
ScrollRoller
/**
* @param {Object} [option]
* @param {HTMLElement} [option.element]
* @param {String} [option.axis = "y"] --- "x" || "y" || "xy"
* @param {Number} [option.pageX = 0]
* @param {Number} [option.pageY = 0]
* @param {Number} [option.acceleration = 0.15]
* @param {Function} [option.endedCallback]
* @constructor
*/
function ScrollRoller(option) {
this.option = Object(option);
this.element = document;
this.axis = "y";
this.pageX = 0;
this.pageY = 0;
this.acceleration = 0.15;
this.endedCallback = null;
this.differ = 0.5;
this.pointer = null;
this.rebuildProp();
this.fillPointer();
}
ScrollRoller.prototype = {
constructor: ScrollRoller,
/** @private */
singleton: [],
/** @private */
rebuildProp: function () {
if (this.option.element && this.option.element.ownerDocument) {
this.element = this.option.element;
}
if (this.option.axis && ["x", "y", "xy"].indexOf(this.option.axis) !== -1) {
this.axis = this.option.axis;
}
if (this.option.pageX) {
this.pageX = Math.round(this.option.pageX) || this.pageX;
}
if (this.option.pageY) {
this.pageY = Math.round(this.option.pageY) || this.pageY;
}
if (this.option.acceleration) {
this.acceleration = Math.abs(this.option.acceleration) || this.acceleration;
}
if (this.option.endedCallback) {
this.endedCallback = this.option.endedCallback;
}
},
/** @private */
fillPointer: function () {
var index = this.findCacheIndex();
if (index === -1) {
this.pointer = {element: this.element};
this.singleton.push(this.pointer);
} else {
this.pointer = this.singleton[index];
}
},
/** @private */
findCacheIndex: function () {
var index = -1;
for (var i = 0; i < this.singleton.length; i++) {
if (this.singleton[i].element === this.element) {
index = i;
break;
}
}
return index;
},
/** @public */
requestScrollFrame: function () {
var speed = 1 + this.acceleration;
var preX = null;
var preY = null;
var xSup = this.pageX + this.differ;
var xSub = this.pageX - this.differ;
var ySup = this.pageY + this.differ;
var ySub = this.pageY - this.differ;
var loop = (function () {
var x1, y1, x2, y2, x3, y3;
var cx, cy, dx, dy, tx, ty;
var ended = false;
if (this.element === document) {
x1 = document.body.scrollLeft || 0;
y1 = document.body.scrollTop || 0;
x2 = document.documentElement.scrollLeft || 0;
y2 = document.documentElement.scrollTop || 0;
x3 = document.defaultView.scrollX || 0;
y3 = document.defaultView.scrollY || 0;
cx = Math.max(x1, x2, x3);
cy = Math.max(y1, y2, y3);
} else {
cx = this.element.scrollLeft || 0;
cy = this.element.scrollTop || 0;
}
switch (this.axis) {
case "x":
ended = cx > xSub && cx < xSup;
break;
case "y":
ended = cy > ySub && cy < ySup;
break;
case "xy":
ended = cx > xSub && cx < xSup && cy > ySub && cy < ySup;
break;
}
dx = cx - this.pageX;
dy = cy - this.pageY;
tx = this.axis === "x" || this.axis === "xy" ? Math[dx > 0 ? "floor" : "ceil"](dx / speed + this.pageX) : cx;
ty = this.axis === "y" || this.axis === "xy" ? Math[dy > 0 ? "floor" : "ceil"](dy / speed + this.pageY) : cy;
if (preX !== null && preX === tx && preY !== null && preY === ty) {
ended = true;
}
preX = tx;
preY = ty;
if (this.element === document) {
document.defaultView.scroll(tx, ty);
} else {
this.element.scrollLeft = tx;
this.element.scrollTop = ty;
}
if (ended) {
typeof this.endedCallback === "function" && this.endedCallback();
} else {
this.pointer.requestId = requestAnimationFrame(loop);
}
}).bind(this);
this.cancelScrollFrame();
loop();
},
/** @public */
cancelScrollFrame: function () {
this.pointer.requestId && cancelAnimationFrame(this.pointer.requestId);
},
/** @public */
destroy: function () {
var index = this.findCacheIndex();
if (index !== -1) {
this.singleton.splice(index, 1);
}
this.cancelScrollFrame();
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment