epubjs
Version:
Parse and Render Epubs
340 lines (284 loc) • 8.79 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _core = require("../../utils/core");
var _constants = require("../../utils/constants");
var _eventEmitter = _interopRequireDefault(require("event-emitter"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// easing equations from https://github.com/danro/easing-js/blob/master/easing.js
const PI_D2 = Math.PI / 2;
const EASING_EQUATIONS = {
easeOutSine: function (pos) {
return Math.sin(pos * PI_D2);
},
easeInOutSine: function (pos) {
return -0.5 * (Math.cos(Math.PI * pos) - 1);
},
easeInOutQuint: function (pos) {
if ((pos /= 0.5) < 1) {
return 0.5 * Math.pow(pos, 5);
}
return 0.5 * (Math.pow(pos - 2, 5) + 2);
},
easeInCubic: function (pos) {
return Math.pow(pos, 3);
}
};
class Snap {
constructor(manager, options) {
this.settings = (0, _core.extend)({
duration: 80,
minVelocity: 0.2,
minDistance: 10,
easing: EASING_EQUATIONS['easeInCubic']
}, options || {});
this.supportsTouch = this.supportsTouch();
if (this.supportsTouch) {
this.setup(manager);
}
}
setup(manager) {
this.manager = manager;
this.layout = this.manager.layout;
this.fullsize = this.manager.settings.fullsize;
if (this.fullsize) {
this.element = this.manager.stage.element;
this.scroller = window;
this.disableScroll();
} else {
this.element = this.manager.stage.container;
this.scroller = this.element;
this.element.style["WebkitOverflowScrolling"] = "touch";
} // this.overflow = this.manager.overflow;
// set lookahead offset to page width
this.manager.settings.offset = this.layout.width;
this.manager.settings.afterScrolledTimeout = this.settings.duration * 2;
this.isVertical = this.manager.settings.axis === "vertical"; // disable snapping if not paginated or axis in not horizontal
if (!this.manager.isPaginated || this.isVertical) {
return;
}
this.touchCanceler = false;
this.resizeCanceler = false;
this.snapping = false;
this.scrollLeft;
this.scrollTop;
this.startTouchX = undefined;
this.startTouchY = undefined;
this.startTime = undefined;
this.endTouchX = undefined;
this.endTouchY = undefined;
this.endTime = undefined;
this.addListeners();
}
supportsTouch() {
if ('ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch) {
return true;
}
return false;
}
disableScroll() {
this.element.style.overflow = "hidden";
}
enableScroll() {
this.element.style.overflow = "";
}
addListeners() {
this._onResize = this.onResize.bind(this);
window.addEventListener('resize', this._onResize);
this._onScroll = this.onScroll.bind(this);
this.scroller.addEventListener('scroll', this._onScroll);
this._onTouchStart = this.onTouchStart.bind(this);
this.scroller.addEventListener('touchstart', this._onTouchStart, {
passive: true
});
this.on('touchstart', this._onTouchStart);
this._onTouchMove = this.onTouchMove.bind(this);
this.scroller.addEventListener('touchmove', this._onTouchMove, {
passive: true
});
this.on('touchmove', this._onTouchMove);
this._onTouchEnd = this.onTouchEnd.bind(this);
this.scroller.addEventListener('touchend', this._onTouchEnd, {
passive: true
});
this.on('touchend', this._onTouchEnd);
this._afterDisplayed = this.afterDisplayed.bind(this);
this.manager.on(_constants.EVENTS.MANAGERS.ADDED, this._afterDisplayed);
}
removeListeners() {
window.removeEventListener('resize', this._onResize);
this._onResize = undefined;
this.scroller.removeEventListener('scroll', this._onScroll);
this._onScroll = undefined;
this.scroller.removeEventListener('touchstart', this._onTouchStart, {
passive: true
});
this.off('touchstart', this._onTouchStart);
this._onTouchStart = undefined;
this.scroller.removeEventListener('touchmove', this._onTouchMove, {
passive: true
});
this.off('touchmove', this._onTouchMove);
this._onTouchMove = undefined;
this.scroller.removeEventListener('touchend', this._onTouchEnd, {
passive: true
});
this.off('touchend', this._onTouchEnd);
this._onTouchEnd = undefined;
this.manager.off(_constants.EVENTS.MANAGERS.ADDED, this._afterDisplayed);
this._afterDisplayed = undefined;
}
afterDisplayed(view) {
let contents = view.contents;
["touchstart", "touchmove", "touchend"].forEach(e => {
contents.on(e, ev => this.triggerViewEvent(ev, contents));
});
}
triggerViewEvent(e, contents) {
this.emit(e.type, e, contents);
}
onScroll(e) {
this.scrollLeft = this.fullsize ? window.scrollX : this.scroller.scrollLeft;
this.scrollTop = this.fullsize ? window.scrollY : this.scroller.scrollTop;
}
onResize(e) {
this.resizeCanceler = true;
}
onTouchStart(e) {
let {
screenX,
screenY
} = e.touches[0];
if (this.fullsize) {
this.enableScroll();
}
this.touchCanceler = true;
if (!this.startTouchX) {
this.startTouchX = screenX;
this.startTouchY = screenY;
this.startTime = this.now();
}
this.endTouchX = screenX;
this.endTouchY = screenY;
this.endTime = this.now();
}
onTouchMove(e) {
let {
screenX,
screenY
} = e.touches[0];
let deltaY = Math.abs(screenY - this.endTouchY);
this.touchCanceler = true;
if (!this.fullsize && deltaY < 10) {
this.element.scrollLeft -= screenX - this.endTouchX;
}
this.endTouchX = screenX;
this.endTouchY = screenY;
this.endTime = this.now();
}
onTouchEnd(e) {
if (this.fullsize) {
this.disableScroll();
}
this.touchCanceler = false;
let swipped = this.wasSwiped();
if (swipped !== 0) {
this.snap(swipped);
} else {
this.snap();
}
this.startTouchX = undefined;
this.startTouchY = undefined;
this.startTime = undefined;
this.endTouchX = undefined;
this.endTouchY = undefined;
this.endTime = undefined;
}
wasSwiped() {
let snapWidth = this.layout.pageWidth * this.layout.divisor;
let distance = this.endTouchX - this.startTouchX;
let absolute = Math.abs(distance);
let time = this.endTime - this.startTime;
let velocity = distance / time;
let minVelocity = this.settings.minVelocity;
if (absolute <= this.settings.minDistance || absolute >= snapWidth) {
return 0;
}
if (velocity > minVelocity) {
// previous
return -1;
} else if (velocity < -minVelocity) {
// next
return 1;
}
}
needsSnap() {
let left = this.scrollLeft;
let snapWidth = this.layout.pageWidth * this.layout.divisor;
return left % snapWidth !== 0;
}
snap(howMany = 0) {
let left = this.scrollLeft;
let snapWidth = this.layout.pageWidth * this.layout.divisor;
let snapTo = Math.round(left / snapWidth) * snapWidth;
if (howMany) {
snapTo += howMany * snapWidth;
}
return this.smoothScrollTo(snapTo);
}
smoothScrollTo(destination) {
const deferred = new _core.defer();
const start = this.scrollLeft;
const startTime = this.now();
const duration = this.settings.duration;
const easing = this.settings.easing;
this.snapping = true; // add animation loop
function tick() {
const now = this.now();
const time = Math.min(1, (now - startTime) / duration);
const timeFunction = easing(time);
if (this.touchCanceler || this.resizeCanceler) {
this.resizeCanceler = false;
this.snapping = false;
deferred.resolve();
return;
}
if (time < 1) {
window.requestAnimationFrame(tick.bind(this));
this.scrollTo(start + (destination - start) * time, 0);
} else {
this.scrollTo(destination, 0);
this.snapping = false;
deferred.resolve();
}
}
tick.call(this);
return deferred.promise;
}
scrollTo(left = 0, top = 0) {
if (this.fullsize) {
window.scroll(left, top);
} else {
this.scroller.scrollLeft = left;
this.scroller.scrollTop = top;
}
}
now() {
return 'now' in window.performance ? performance.now() : new Date().getTime();
}
destroy() {
if (!this.scroller) {
return;
}
if (this.fullsize) {
this.enableScroll();
}
this.removeListeners();
this.scroller = undefined;
}
}
(0, _eventEmitter.default)(Snap.prototype);
var _default = Snap;
exports.default = _default;