_import Debugger from './Debugger.js';
const debug = new Debugger('[DBG:SW]', true);

/**
 * Scroll event watcher for smooth animation.
 * @author Satoshi Soma (amekusa.com)
 * @license Apache-2.0
 * Copyright 2020 Satoshi Soma
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class ScrollWatcher {
	/**
	 * @param {Element} [target=window] - Element to watch
	 */
	constructor(target = window) {
		this.target = target;
		this.tasks = {
			init: [],
			scroll: [],
			resize: [],
			scrollend: [],
		};
	}
	/**
	 * Registers a callback.
	 * @param {string|string[]} ev - Event name(s). Pass `any` to register to all the available events
	 * @param {function} fn - Callback
	 * @example
	 * let sw.on('scroll', ev => {
	 *   console.log('Scroll Detected');
	 * });
	 */
	on(ev, fn) {
		if (Array.isArray(ev)) {
			for (let i = 0; i < ev.length; i++) this.on(ev[i], fn);
		} else if (ev == 'any') {
			for (let key in this.tasks) this.on(key, fn);
		} else this.tasks[ev].push(fn);
	}
	/**
	 * Starts watching scroll related events.
	 * @param {string|string[]} ev - Event name(s) to watch. `any` to watch all the available events
	 */
	watch(ev = 'any') {
		if (ev == 'any') ev = Object.keys(this.tasks);
		else if (!Array.isArray(ev)) ev = [ev];

		// context
		let c = new Stats({ x: 0, y: 0, mx: 0, my: 0, time: 0 });
		c.isFirst = true;
		c.event = null;

		let request = false; // animation frame request id
		let tick = time => {
			c.set('time', time);
			debug.log(`animation frame #${request} started @`, time);
			debug.log(' - diff:', c.diff.time);
			let tasks = this.tasks[c.event.type];
			for (let i = 0; i < tasks.length; i++) tasks[i](c);
			if (c.isFirst) c.isFirst = false;
			debug.log(`animation frame #${request} done`);
			request = false;
		};
		let propX, propY, propMX, propMY;
		if (this.target === window) {
			propX = 'scrollX';
			propY = 'scrollY';
			propMX = 'scrollMaxX';
			propMY = 'scrollMaxY';
		} else {
			propX = 'scrollLeft';
			propY = 'scrollTop';
			propMX = 'scrollLeftMax';
			propMY = 'scrollTopMax';
		}
		let handler = ev => {
			debug.log(`--- ${ev.type} event ---`);
			if (request) { // previous request is still in the queue
				window.cancelAnimationFrame(request); // cancel the previous request
				debug.log(`<BUSY!> canceled animation frame #${request}`);
			};
			c.event = ev;
			c.set('x', this.target[propX]);
			c.set('y', this.target[propY]);
			c.set('mx', this.target[propMX]);
			c.set('my', this.target[propMY]);
			request = window.requestAnimationFrame(tick);
			debug.log(`animation frame #${request} requested`);
		};
		for (let i = 0; i < ev.length; i++) {
			switch (ev[i]) {
				case   'init': handler({ type: 'init' }); break; // fake event
				case 'resize': window.addEventListener('resize', handler); break;
				default: this.target.addEventListener(ev[i], handler);
			}
		}
	}
}

class Stats {
	constructor(data) {
		this.curr = {};
		this.prev = {};
		this.diff = {};
		for (let key in data) {
			this.curr[key] = data[key];
			this.prev[key] = undefined;
			this.diff[key] = undefined;
		}
	}
	get(key) {
		return this.curr[key];
	}
	set(key, value) {
		this.prev[key] = this.curr[key];
		this.curr[key] = value;
		this.diff[key] = this.curr[key] - this.prev[key];
		return this;
	}
}

export default ScrollWatcher;

Licensed under the Apache License 2.0

Documentation generated by JSDoc 4.0.2 using Docolatte theme