70 lines
2.4 KiB
JavaScript
70 lines
2.4 KiB
JavaScript
// src/resources/js/scrollfx/ScrollTrigger.js
|
|
|
|
export default class ScrollTrigger {
|
|
constructor(config) {
|
|
this.element = this.resolveElement(config.element);
|
|
this.target = config.target
|
|
? this.element.querySelector(config.target)
|
|
: this.element;
|
|
|
|
if (config.target && !this.target) {
|
|
throw new Error(`Target selector '${config.target}' not found inside element '${config.element}'.`);
|
|
}
|
|
|
|
this.start = config.start || 'top 80%';
|
|
this.end = config.end || 'bottom 20%';
|
|
this.scrub = config.scrub || false;
|
|
this.onEnter = config.onEnter || null;
|
|
this.onLeave = config.onLeave || null;
|
|
this.onUpdate = config.onUpdate || null;
|
|
|
|
this._wasVisible = false;
|
|
this._progress = 0;
|
|
}
|
|
|
|
resolveElement(input) {
|
|
if (typeof input === 'string') {
|
|
const els = document.querySelectorAll(input);
|
|
if (els.length === 1) return els[0];
|
|
throw new Error(`Selector '${input}' matched ${els.length} elements, expected exactly 1.`);
|
|
}
|
|
return input;
|
|
}
|
|
|
|
getScrollProgress(viewportHeight) {
|
|
const rect = this.element.getBoundingClientRect();
|
|
const startPx = this.parsePosition(this.start, viewportHeight);
|
|
const endPx = this.parsePosition(this.end, viewportHeight);
|
|
const scrollRange = endPx - startPx;
|
|
const current = rect.top - startPx;
|
|
return 1 - Math.min(Math.max(current / scrollRange, 0), 1);
|
|
}
|
|
|
|
parsePosition(pos, viewportHeight) {
|
|
const [edge, value] = pos.split(' ');
|
|
const edgeOffset = edge === 'top' ? 0 : viewportHeight;
|
|
const percentage = parseFloat(value) / 100;
|
|
return edgeOffset - viewportHeight * percentage;
|
|
}
|
|
|
|
update(viewportHeight) {
|
|
const rect = this.element.getBoundingClientRect();
|
|
const inViewport = rect.bottom > 0 && rect.top < viewportHeight;
|
|
|
|
if (inViewport && !this._wasVisible) {
|
|
this._wasVisible = true;
|
|
if (this.onEnter) this.onEnter(this.target);
|
|
}
|
|
|
|
if (!inViewport && this._wasVisible) {
|
|
this._wasVisible = false;
|
|
if (this.onLeave) this.onLeave(this.target);
|
|
}
|
|
|
|
if (this.scrub && inViewport) {
|
|
const progress = this.getScrollProgress(viewportHeight);
|
|
if (this.onUpdate) this.onUpdate(this.target, progress);
|
|
}
|
|
}
|
|
}
|