@adoratorio/apollo
Version:
A JS library for custom cursor
210 lines • 7.29 kB
JavaScript
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,
};
const dt = delta || 1;
this._velocity = {
x: (this.cursorPosition.x - this.cursorPositionPrev.x) / dt,
y: (this.cursorPosition.y - this.cursorPositionPrev.y) / dt,
};
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