UNPKG

@adoratorio/apollo

Version:

A JS library for custom cursor

209 lines 7.26 kB
import Aion from '@adoratorio/aion'; import Easings from './easing'; import { createProp } from './utils'; class Apollo { static EASING = Easings; options; mousePosition; mouseRenderPosition; _trackMouse; cursorPosition; cursorPositionPrev = { x: 0, y: 0 }; _velocity = { x: 0, y: 0 }; _direction = { x: 0, y: 0 }; engine; frameHandler; cursorXTimeline; cursorYTimeline; aionId = `apollo-frame-${performance.now()}`; plugins = []; internalId = 0; constructor(options) { createProp(); const defaults = { easing: { mode: Apollo.EASING.CUBIC, duration: 1000, }, initialPosition: { x: 0, y: 0 }, detectTouch: false, aion: null, }; this.options = { ...defaults, ...options }; this.mousePosition = this.options.initialPosition; this.mouseRenderPosition = this.mousePosition; this.cursorPosition = this.mousePosition; this.cursorXTimeline = { start: 0, duration: this.options.easing.duration, initial: this.cursorPosition.x, current: this.cursorPosition.x, final: this.cursorPosition.x, }; this.cursorYTimeline = { start: 0, duration: this.options.easing.duration, initial: this.cursorPosition.y, current: this.cursorPosition.y, final: this.cursorPosition.y, }; if (this.options.aion !== null) { this.engine = this.options.aion; } else { this.engine = new Aion(); this.engine.start(); } this.frameHandler = (delta) => { this.frame(delta); }; this.engine.add(this.frameHandler, this.aionId); this.bindEvents(); this._trackMouse = true; } frame = (delta) => { this.plugins.forEach((plugin) => plugin.preFrame && plugin.preFrame(this, delta)); this.cursorXTimeline.final = this.mouseRenderPosition.x; this.cursorYTimeline.final = this.mouseRenderPosition.y; const deltaT = Math.min(Math.max(delta, 0), this.options.easing.duration); const time = this.options.easing.mode(deltaT / this.options.easing.duration); this.cursorXTimeline.current = this.cursorXTimeline.initial + (time * (this.cursorXTimeline.final - this.cursorXTimeline.initial)); this.cursorYTimeline.current = this.cursorYTimeline.initial + (time * (this.cursorYTimeline.final - this.cursorYTimeline.initial)); this.cursorPosition = { x: this.cursorXTimeline.current, y: this.cursorYTimeline.current, }; this._velocity = { x: (this.cursorPosition.x - this.cursorPositionPrev.x) / delta, y: (this.cursorPosition.y - this.cursorPositionPrev.y) / delta, }; this._direction = { x: this._velocity.x > 0 ? 1 : -1, y: this._velocity.y > 0 ? 1 : -1, }; this.velocity.x = Math.abs(this._velocity.x); this.velocity.y = Math.abs(this._velocity.y); this.plugins.forEach((plugin) => plugin.frame && plugin.frame(this, delta)); this.cursorXTimeline.initial = this.cursorXTimeline.current; this.cursorYTimeline.initial = this.cursorYTimeline.current; this.cursorPositionPrev = this.cursorPosition; this.plugins.forEach((plugin) => plugin.afterFrame && plugin.afterFrame(this, delta)); }; bindEvents() { document.body.addEventListener('pointermove', this.mouseMove, { passive: true }); if (this.options.detectTouch) { document.body.addEventListener('touchstart', this.touchMove, { passive: true }); document.body.addEventListener('touchmove', this.touchMove, { passive: true }); } } mouseMove = (event) => { this.mousePosition = { x: event.clientX, y: event.clientY, }; if (!this._trackMouse) return; this.mouseRenderPosition = this.mousePosition; }; touchMove = (event) => { this.mousePosition = { x: event.touches[0]?.clientX || 0, y: event.touches[0]?.clientY || 0, }; if (!this._trackMouse) return; this.mouseRenderPosition = this.mousePosition; }; register(plugin, id) { if (typeof plugin.register === 'function') plugin.register(this); plugin.id = id; this.plugins.push(plugin); } registerPlugin(plugin, id) { if (!plugin.name) { throw new Error('Plugin must have a name property'); } if (this.plugins.some(p => p.name === plugin.name)) { throw new Error(`Plugin with name "${plugin.name}" is already registered`); } const pluginId = id || `apollo-plugin-${this.internalId++}`; this.register(plugin, pluginId); return pluginId; } unregisterPlugin(id) { const foundIndex = this.plugins.findIndex((p) => p.id === id); if (foundIndex === -1) return false; const found = this.plugins[foundIndex]; if (found && typeof found.destroy === 'function') found.destroy(); this.plugins.splice(foundIndex, 1); return true; } registerPlugins(plugins, ids) { const is = []; plugins.forEach((plugin, index) => { is.push(this.registerPlugin(plugin, ids[index])); }); return is; } unbindEvents() { document.body.removeEventListener('pointermove', this.mouseMove); if (this.options.detectTouch) { document.body.removeEventListener('touchstart', this.touchMove); document.body.removeEventListener('touchmove', this.touchMove); } } destroy() { this.unbindEvents(); this.engine.remove(this.aionId); this.plugins.forEach(plugin => { if (plugin.destroy) plugin.destroy(); }); this.plugins = []; } getPlugin(name) { return this.plugins.find(plugin => plugin.name === name); } get trackMouse() { return this._trackMouse; } set trackMouse(value) { this._trackMouse = value; } startMouseTracking() { this._trackMouse = true; } stopMouseTracking() { this._trackMouse = false; } get coords() { return this.cursorPosition; } set coords(coords) { this.mouseRenderPosition = coords; } get normalizedCoords() { return { x: ((this.cursorPosition.x / window.innerWidth) * 2) - 1, y: ((this.cursorPosition.y / window.innerHeight) * 2) - 1, }; } get mouse() { return this.mousePosition; } get normalizedMouse() { return { x: ((this.mousePosition.x / window.innerWidth) * 2) - 1, y: ((this.mousePosition.y / window.innerHeight) * 2) - 1, }; } get velocity() { return this._velocity; } get direction() { return this._direction; } } export default Apollo; //# sourceMappingURL=index.js.map