UNPKG

scrollcat

Version:

I want to catch the best scene of my life. The browser wants to, too.

407 lines (399 loc) 12.4 kB
var __accessCheck = (obj, member, msg) => { if (!member.has(obj)) throw TypeError("Cannot " + msg); }; var __privateGet = (obj, member, getter) => { __accessCheck(obj, member, "read from private field"); return getter ? getter.call(obj) : member.get(obj); }; var __privateAdd = (obj, member, value) => { if (member.has(obj)) throw TypeError("Cannot add the same private member more than once"); member instanceof WeakSet ? member.add(obj) : member.set(obj, value); }; var __privateSet = (obj, member, value, setter) => { __accessCheck(obj, member, "write to private field"); setter ? setter.call(obj, value) : member.set(obj, value); return value; }; var __privateMethod = (obj, member, method) => { __accessCheck(obj, member, "access private method"); return method; }; // src/deferred.ts function deferred() { let methods; let state = "pending"; const promise = new Promise((resolve, reject) => { methods = { async resolve(value) { await value; state = "fulfilled"; resolve(value); }, reject(reason) { state = "rejected"; reject(reason); } }; }); Object.defineProperty(promise, "state", { get: () => state }); return Object.assign(promise, methods); } // src/queue.ts var _list, _limit, _top, _bottom, _destroyed, _move, move_fn, _init, init_fn; var Queue = class { constructor(limit = 1024) { __privateAdd(this, _move); __privateAdd(this, _init); __privateAdd(this, _list, void 0); __privateAdd(this, _limit, void 0); __privateAdd(this, _top, 0); __privateAdd(this, _bottom, 0); __privateAdd(this, _destroyed, false); __privateSet(this, _limit, limit); __privateMethod(this, _init, init_fn).call(this); } async *[Symbol.asyncIterator]() { for (; !__privateGet(this, _destroyed); ) { yield this.pop(); } } pop() { const dataDeferred = __privateGet(this, _list)[__privateGet(this, _top)]; __privateSet(this, _top, __privateMethod(this, _move, move_fn).call(this, __privateGet(this, _top))); return dataDeferred; } push(data) { __privateGet(this, _list)[__privateGet(this, _bottom)].resolve(data); __privateSet(this, _bottom, __privateMethod(this, _move, move_fn).call(this, __privateGet(this, _bottom))); __privateGet(this, _list)[__privateGet(this, _bottom)] = deferred(); } refresh() { __privateMethod(this, _init, init_fn).call(this); } destroy() { if (!__privateGet(this, _destroyed)) { __privateSet(this, _destroyed, true); __privateGet(this, _list)[__privateGet(this, _bottom)].resolve(); } } }; _list = new WeakMap(); _limit = new WeakMap(); _top = new WeakMap(); _bottom = new WeakMap(); _destroyed = new WeakMap(); _move = new WeakSet(); move_fn = function(n) { return n === __privateGet(this, _limit) ? 0 : n + 1; }; _init = new WeakSet(); init_fn = function() { __privateSet(this, _list, new Array(__privateGet(this, _limit) + 100)); __privateGet(this, _list)[0] = deferred(); __privateSet(this, _top, __privateSet(this, _bottom, 0)); }; // src/consts.ts var ScrollKeys = { " ": 1, "Tab": 1, "ArrowDown": 1, "ArrowLeft": 1, "ArrowRight": 1, "ArrowUp": 1, "End": 1, "Home": 1, "PageDown": 1, "PageUp": 1 }; // src/utils.scroll-lock.ts function preventDefault(e) { e.preventDefault(); } function preventDefaultForScrollKeys(e) { if (ScrollKeys[e.key]) { preventDefault(e); return false; } } var wheelOpt = { passive: false }; var wheelEvent = "onwheel" in document.createElement("div") ? "wheel" : "mousewheel"; var lockScroll = (el) => { el.addEventListener("DOMMouseScroll", preventDefault, false); el.addEventListener(wheelEvent, preventDefault, wheelOpt); el.addEventListener("touchmove", preventDefault, wheelOpt); el.addEventListener("keydown", preventDefaultForScrollKeys, false); }; var unlockScroll = (el) => { el.removeEventListener("DOMMouseScroll", preventDefault, false); el.removeEventListener(wheelEvent, preventDefault, false); el.removeEventListener("touchmove", preventDefault, false); el.removeEventListener("keydown", preventDefaultForScrollKeys, false); }; // src/utils.ts var p2nMap = { "100%": 1 }; function p2n(p) { if (p2nMap[p] !== void 0) { return p2nMap[p]; } return p2nMap[p] = Number(p.slice(0, p.length - 1)) / 100; } // src/scene.ts var DefaultSceneConfig = { play: "100%", duration: "100%" }; var _scroller, _el, _events, _eventsMap, _prevState, _state, _cfg, _rect, _playStart, _stateChanged, stateChanged_get, _isEnter, isEnter_get, _isLeave, isLeave_get, _preUpdate, preUpdate_fn, _trigger, trigger_fn; var Scene = class { constructor(scroller, el, cfg) { __privateAdd(this, _stateChanged); __privateAdd(this, _isEnter); __privateAdd(this, _isLeave); __privateAdd(this, _preUpdate); __privateAdd(this, _trigger); __privateAdd(this, _scroller, void 0); __privateAdd(this, _el, void 0); __privateAdd(this, _events, new Queue()); __privateAdd(this, _eventsMap, /* @__PURE__ */ new Map()); __privateAdd(this, _prevState, void 0); __privateAdd(this, _state, void 0); __privateAdd(this, _cfg, void 0); __privateAdd(this, _rect, void 0); __privateAdd(this, _playStart, void 0); this.isActive = true; __privateSet(this, _scroller, scroller); __privateSet(this, _el, el); __privateSet(this, _cfg, cfg); __privateMethod(this, _preUpdate, preUpdate_fn).call(this); } get emitter() { return __privateGet(this, _events); } get scrollTop() { return __privateGet(this, _playStart) - __privateGet(this, _rect).top; } get progress() { return this.scrollTop / this.duration; } get state() { return __privateGet(this, _state); } update() { __privateMethod(this, _preUpdate, preUpdate_fn).call(this); const event = { target: __privateGet(this, _el), scrollTop: this.scrollTop, progress: this.progress }; __privateMethod(this, _trigger, trigger_fn).call(this, "update", event); if (__privateGet(this, _isEnter, isEnter_get)) { __privateMethod(this, _trigger, trigger_fn).call(this, "enter", event); } else if (__privateGet(this, _isLeave, isLeave_get)) { __privateMethod(this, _trigger, trigger_fn).call(this, "leave", event); } } removeSelf() { __privateGet(this, _events).destroy(); __privateGet(this, _scroller).removeScene(this); } scrollTo(n, cfg) { if (typeof n === "string") { n = this.duration * p2n(n); } __privateGet(this, _scroller).scrollTo(__privateGet(this, _el).offsetTop - __privateGet(this, _playStart) + n, cfg); } on(eventType, cb) { let cbs = __privateGet(this, _eventsMap).get(eventType); if (!cbs) { cbs = /* @__PURE__ */ new Set(); __privateGet(this, _eventsMap).set(eventType, cbs); } cbs.add(cb); } off(eventType, cb) { const cbs = __privateGet(this, _eventsMap).get(eventType); if (!(cbs == null ? void 0 : cbs.size)) { return; } if (cb) { cbs.delete(cb); } else { cbs.clear(); } } once(eventType, cb) { const handler = (ev) => { cb(ev); this.off(eventType, handler); }; this.on(eventType, handler); } }; _scroller = new WeakMap(); _el = new WeakMap(); _events = new WeakMap(); _eventsMap = new WeakMap(); _prevState = new WeakMap(); _state = new WeakMap(); _cfg = new WeakMap(); _rect = new WeakMap(); _playStart = new WeakMap(); _stateChanged = new WeakSet(); stateChanged_get = function() { return __privateGet(this, _prevState) !== __privateGet(this, _state); }; _isEnter = new WeakSet(); isEnter_get = function() { if (__privateGet(this, _stateChanged, stateChanged_get)) { const direction = __privateGet(this, _scroller).direction; return direction === "forward" && __privateGet(this, _prevState) === "before" || direction === "reverse" && __privateGet(this, _prevState) === "after"; } return false; }; _isLeave = new WeakSet(); isLeave_get = function() { return __privateGet(this, _stateChanged, stateChanged_get) && __privateGet(this, _prevState) === "during"; }; _preUpdate = new WeakSet(); preUpdate_fn = function() { const clientHeight = __privateGet(this, _scroller).clientHeight; const rect = __privateSet(this, _rect, __privateGet(this, _el).getBoundingClientRect()); const { top, bottom } = rect; let playStart = __privateGet(this, _cfg).play; if (typeof playStart === "string") { playStart = clientHeight * p2n(playStart); } __privateSet(this, _playStart, playStart); let duration = __privateGet(this, _cfg).duration; if (typeof duration === "string") { duration = (bottom - top) * p2n(duration); } this.duration = duration; __privateSet(this, _prevState, __privateGet(this, _state)); const newState = top <= playStart && top + duration >= playStart ? "during" : top < playStart ? "after" : "before"; if (!__privateGet(this, _state)) { __privateSet(this, _prevState, newState); } __privateSet(this, _state, newState); }; _trigger = new WeakSet(); trigger_fn = function(type, event) { __privateGet(this, _events).push([type, event]); const cbs = __privateGet(this, _eventsMap).get(type); if (cbs) { for (const cb of cbs) { cb(event); } } }; // src/scroller.ts var doc = self.document; var _el2, _scenes, _prevScrollTop, _locker, _update, update_fn; var Scroller = class { constructor(el) { __privateAdd(this, _update); __privateAdd(this, _el2, void 0); __privateAdd(this, _scenes, /* @__PURE__ */ new Set()); __privateAdd(this, _prevScrollTop, void 0); __privateAdd(this, _locker, void 0); __privateSet(this, _el2, el); doc.addEventListener("scroll", __privateMethod(this, _update, update_fn).bind(this)); this.destroy = () => { doc.removeEventListener("scroll", __privateMethod(this, _update, update_fn).bind(this)); for (const scene of __privateGet(this, _scenes)) { this.removeScene(scene); } this.unlock(); }; } get clientHeight() { return self.innerHeight || doc.documentElement.clientHeight; } get locked() { return !!__privateGet(this, _locker); } get el() { return __privateGet(this, _el2) || doc.body; } addScene(el, cfg) { const scene = new Scene(this, el, cfg); __privateGet(this, _scenes).add(scene); return scene; } addSceneWithDefault(el) { return this.addScene(el, DefaultSceneConfig); } removeScene(scene) { if (__privateGet(this, _scenes).has(scene)) { __privateGet(this, _scenes).delete(scene); scene.removeSelf(); } } scrollTo(n, cfg = {}) { if (typeof n === "string") { n = p2n(n); } if (__privateGet(this, _el2) === void 0) { self.scrollTo({ top: n, ...cfg }); } __privateMethod(this, _update, update_fn).call(this); } lock(duration) { if (__privateGet(this, _locker)) { this.unlock(); } const future = deferred(); const currentScrollTop = this.scrollTop; lockScroll(this.el); let ticking = false; __privateSet(this, _locker, [ setInterval(() => { if (!ticking) { self.requestAnimationFrame(() => { this.scrollTo(currentScrollTop); ticking = false; }); ticking = true; } }, 0), future ]); if (duration) { setTimeout(this.unlock.bind(this), duration); } return future; } unlock() { unlockScroll(this.el); if (__privateGet(this, _locker)) { clearInterval(__privateGet(this, _locker)[0]); __privateGet(this, _locker)[1].resolve(); __privateSet(this, _locker, void 0); } } }; _el2 = new WeakMap(); _scenes = new WeakMap(); _prevScrollTop = new WeakMap(); _locker = new WeakMap(); _update = new WeakSet(); update_fn = function() { __privateSet(this, _prevScrollTop, this.scrollTop); this.scrollTop = self.scrollY; if (!__privateGet(this, _prevScrollTop)) { __privateSet(this, _prevScrollTop, this.scrollTop); } const delta = this.scrollTop - __privateGet(this, _prevScrollTop); this.direction = delta > 0 ? "forward" : delta < 0 ? "reverse" : "paused"; for (const scene of __privateGet(this, _scenes)) { if (scene.isActive) { scene.update(); } } }; export { DefaultSceneConfig, Scene, Scroller };