smooth-scrollbar
Version:
Customize scrollbar in modern browsers with smooth scrolling experience.
255 lines • 9.43 kB
JavaScript
import { __assign, __extends } from "tslib";
import { clamp, debounce } from '../../utils';
import { ScrollbarPlugin } from 'smooth-scrollbar';
import { Bounce } from './bounce';
import { Glow } from './glow';
export var OverscrollEffect;
(function (OverscrollEffect) {
OverscrollEffect["BOUNCE"] = "bounce";
OverscrollEffect["GLOW"] = "glow";
})(OverscrollEffect || (OverscrollEffect = {}));
var ALLOWED_EVENTS = /wheel|touch/;
var OverscrollPlugin = /** @class */ (function (_super) {
__extends(OverscrollPlugin, _super);
function OverscrollPlugin() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this._glow = new Glow(_this.scrollbar);
_this._bounce = new Bounce(_this.scrollbar);
_this._wheelScrollBack = {
x: false,
y: false,
};
_this._lockWheel = {
x: false,
y: false,
};
_this._touching = false;
_this._amplitude = {
x: 0,
y: 0,
};
_this._position = {
x: 0,
y: 0,
};
// since we can't detect whether user release touchpad
// handle it with debounce is the best solution now, as a trade-off
_this._releaseWheel = debounce(function () {
_this._lockWheel.x = false;
_this._lockWheel.y = false;
}, 30);
return _this;
}
Object.defineProperty(OverscrollPlugin.prototype, "_isWheelLocked", {
get: function () {
return this._lockWheel.x || this._lockWheel.y;
},
enumerable: true,
configurable: true
});
Object.defineProperty(OverscrollPlugin.prototype, "_enabled", {
get: function () {
return !!this.options.effect;
},
enumerable: true,
configurable: true
});
OverscrollPlugin.prototype.onInit = function () {
var _a = this, _glow = _a._glow, options = _a.options, scrollbar = _a.scrollbar;
// observe
var effect = options.effect;
Object.defineProperty(options, 'effect', {
get: function () {
return effect;
},
set: function (val) {
if (!val) {
effect = undefined;
return;
}
if (val !== OverscrollEffect.BOUNCE && val !== OverscrollEffect.GLOW) {
throw new TypeError("unknow overscroll effect: " + val);
}
effect = val;
scrollbar.options.continuousScrolling = false;
if (val === OverscrollEffect.GLOW) {
_glow.mount();
_glow.adjust();
}
else {
_glow.unmount();
}
},
});
options.effect = effect; // init
};
OverscrollPlugin.prototype.onUpdate = function () {
if (this.options.effect === OverscrollEffect.GLOW) {
this._glow.adjust();
}
};
OverscrollPlugin.prototype.onRender = function (remainMomentum) {
if (!this._enabled) {
return;
}
if (this.scrollbar.options.continuousScrolling) {
// turn off continuous scrolling
this.scrollbar.options.continuousScrolling = false;
}
var nextX = remainMomentum.x, nextY = remainMomentum.y;
// transfer remain momentum to overscroll
if (!this._amplitude.x &&
this._willOverscroll('x', remainMomentum.x)) {
nextX = 0;
this._absorbMomentum('x', remainMomentum.x);
}
if (!this._amplitude.y &&
this._willOverscroll('y', remainMomentum.y)) {
nextY = 0;
this._absorbMomentum('y', remainMomentum.y);
}
this.scrollbar.setMomentum(nextX, nextY);
this._render();
};
OverscrollPlugin.prototype.transformDelta = function (delta, fromEvent) {
this._lastEventType = fromEvent.type;
if (!this._enabled || !ALLOWED_EVENTS.test(fromEvent.type)) {
return delta;
}
if (this._isWheelLocked && /wheel/.test(fromEvent.type)) {
this._releaseWheel();
if (this._willOverscroll('x', delta.x)) {
delta.x = 0;
}
if (this._willOverscroll('y', delta.y)) {
delta.y = 0;
}
}
var nextX = delta.x, nextY = delta.y;
if (this._willOverscroll('x', delta.x)) {
nextX = 0;
this._addAmplitude('x', delta.x);
}
if (this._willOverscroll('y', delta.y)) {
nextY = 0;
this._addAmplitude('y', delta.y);
}
switch (fromEvent.type) {
case 'touchstart':
case 'touchmove':
this._touching = true;
this._glow.recordTouch(fromEvent);
break;
case 'touchcancel':
case 'touchend':
this._touching = false;
break;
}
return {
x: nextX,
y: nextY,
};
};
OverscrollPlugin.prototype._willOverscroll = function (direction, delta) {
if (!delta) {
return false;
}
// away from origin
if (this._position[direction]) {
return true;
}
var offset = this.scrollbar.offset[direction];
var limit = this.scrollbar.limit[direction];
if (limit === 0) {
return false;
}
// cond:
// 1. next scrolling position is supposed to stay unchange
// 2. current position is on the edge
return clamp(offset + delta, 0, limit) === offset &&
(offset === 0 || offset === limit);
};
OverscrollPlugin.prototype._absorbMomentum = function (direction, remainMomentum) {
var _a = this, options = _a.options, _lastEventType = _a._lastEventType, _amplitude = _a._amplitude;
if (!ALLOWED_EVENTS.test(_lastEventType)) {
return;
}
_amplitude[direction] = clamp(remainMomentum, -options.maxOverscroll, options.maxOverscroll);
};
OverscrollPlugin.prototype._addAmplitude = function (direction, delta) {
var _a = this, options = _a.options, scrollbar = _a.scrollbar, _amplitude = _a._amplitude, _position = _a._position;
var currentAmp = _amplitude[direction];
var isOpposite = delta * currentAmp < 0;
var friction;
if (isOpposite) {
// opposite direction
friction = 0;
}
else {
friction = this._wheelScrollBack[direction] ?
1 : Math.abs(currentAmp / options.maxOverscroll);
}
var amp = currentAmp + delta * (1 - friction);
_amplitude[direction] = scrollbar.offset[direction] === 0 ?
/* top | left */ clamp(amp, -options.maxOverscroll, 0) :
/* bottom | right */ clamp(amp, 0, options.maxOverscroll);
if (isOpposite) {
// scroll back
_position[direction] = _amplitude[direction];
}
};
OverscrollPlugin.prototype._render = function () {
var _a = this, options = _a.options, _amplitude = _a._amplitude, _position = _a._position;
if (this._enabled &&
(_amplitude.x || _amplitude.y || _position.x || _position.y)) {
var nextX = this._nextAmp('x');
var nextY = this._nextAmp('y');
_amplitude.x = nextX.amplitude;
_position.x = nextX.position;
_amplitude.y = nextY.amplitude;
_position.y = nextY.position;
switch (options.effect) {
case OverscrollEffect.BOUNCE:
this._bounce.render(_position);
break;
case OverscrollEffect.GLOW:
this._glow.render(_position, this.options.glowColor);
break;
}
if (typeof options.onScroll === 'function') {
options.onScroll.call(this, __assign({}, _position));
}
}
};
OverscrollPlugin.prototype._nextAmp = function (direction) {
var _a = this, options = _a.options, _amplitude = _a._amplitude, _position = _a._position;
var t = 1 - options.damping;
var amp = _amplitude[direction];
var pos = _position[direction];
var nextAmp = this._touching ? amp : (amp * t | 0);
var distance = nextAmp - pos;
var nextPos = pos + distance - (distance * t | 0);
if (!this._touching && Math.abs(nextPos) < Math.abs(pos)) {
this._wheelScrollBack[direction] = true;
}
if (this._wheelScrollBack[direction] && Math.abs(nextPos) <= 1) {
this._wheelScrollBack[direction] = false;
this._lockWheel[direction] = true;
}
return {
amplitude: nextAmp,
position: nextPos,
};
};
OverscrollPlugin.pluginName = 'overscroll';
OverscrollPlugin.defaultOptions = {
effect: OverscrollEffect.BOUNCE,
onScroll: undefined,
damping: 0.2,
maxOverscroll: 150,
glowColor: '#87ceeb',
};
return OverscrollPlugin;
}(ScrollbarPlugin));
export default OverscrollPlugin;
//# sourceMappingURL=index.js.map