vevet
Version:
Vevet is a JavaScript library for creative development that simplifies crafting rich interactions like split text animations, carousels, marquees, preloading, and more.
145 lines • 5.82 kB
JavaScript
import { clamp } from '../../../../../utils';
import { PARALLAX_GROUPS, PARALLAX_TYPES } from './constants';
import { getAttr, getFloatAttr, isParallaxAttr } from './utils';
export class SnapSlideParallax {
constructor(_slide, _element, _getImpulse) {
this._slide = _slide;
this._element = _element;
this._getImpulse = _getImpulse;
this._items = [];
this._debounceInit = null;
this._initDebounce();
this._observer = new MutationObserver((mutations) => {
mutations.forEach(({ attributeName }) => {
if (attributeName && isParallaxAttr(attributeName)) {
this._initDebounce();
}
});
});
this._observer.observe(this._element, { attributes: true });
}
/** Initialize parallax with debounce */
_initDebounce() {
if (this._debounceInit) {
clearTimeout(this._debounceInit);
}
this._debounceInit = setTimeout(() => this._init(), 16);
}
/** Initialize parallax */
_init() {
this._fetchItems();
this.render();
}
/** Fetch parallax items */
_fetchItems() {
const element = this._element;
const defaultScope = this._getScope(this._element, `scope`, [-1, 1]);
const types = PARALLAX_TYPES.filter(({ attr }) => element.hasAttribute(attr));
this._items = types.map(({ attr, prop, unit: defaultUnit, isAbs: isAbsProp, modifier }) => {
var _a;
const group = PARALLAX_GROUPS.find(({ types }) => types.find((type) => type.attr === attr));
const scopeAttr = `${attr}-scope`;
const scope = element.hasAttribute(scopeAttr)
? this._getScope(element, scopeAttr, [-1, 1])
: defaultScope;
const attrValue = getAttr(element, attr);
const unit = attrValue.replace(/[-\d.]+/g, '') || defaultUnit;
const target = getFloatAttr(element, attr, 0);
const offset = getFloatAttr(element, `${attr}-offset`, 0);
const min = getFloatAttr(element, `${attr}-min`, -Infinity);
const max = getFloatAttr(element, `${attr}-max`, Infinity);
const impulseAttr = `${attr}-impulse`;
const impulse = element.hasAttribute(impulseAttr)
? getFloatAttr(element, impulseAttr, 1)
: 0;
// legacy influence
const influenceAttr = `${attr}-influence`;
const influence = element.hasAttribute(influenceAttr)
? getFloatAttr(element, influenceAttr, 1)
: 0;
const directionalAttr = `${attr}-directional`;
const isDirectional = element.hasAttribute(directionalAttr);
const absAttr = `${attr}-abs`;
const isAbs = isAbsProp || element.hasAttribute(absAttr);
return {
attr,
prop,
unit,
group: (_a = group === null || group === void 0 ? void 0 : group.prop) !== null && _a !== void 0 ? _a : '',
modifier,
scope,
progress: 0,
target,
value: 0,
offset,
min,
max,
impulse: impulse || influence,
isDirectional,
isAbs,
};
});
}
/** Get parallax scope */
_getScope(element, suffix, defaultValue) {
const attrValue = getAttr(element, suffix);
const stringValue = attrValue.toLowerCase();
if (stringValue === 'none') {
return [-Infinity, Infinity];
}
if (stringValue === 'const') {
return [1, 1];
}
const cleanValue = attrValue.replace(/[\s\\[\]]+/g, '');
const minMax = cleanValue.split(',');
const minRaw = parseFloat(minMax[0]);
const maxRaw = parseFloat(minMax[1]);
const min = Number.isNaN(minRaw) ? defaultValue[0] : minRaw;
const max = Number.isNaN(maxRaw) ? defaultValue[1] : maxRaw;
return [min, max];
}
/** Render parallax */
render() {
const { _element: element, _items: items, _slide: slide } = this;
const impulse = this._getImpulse();
const globalProgress = slide.progress;
// Calculate parallax values
items.forEach((item) => {
let progress = clamp(globalProgress, ...item.scope);
if (Math.abs(item.impulse) > 0) {
progress *= Math.abs(impulse) * item.impulse;
}
if (item.isDirectional) {
progress = Math.abs(progress) * Math.sign(impulse);
}
if (item.isAbs) {
progress = Math.abs(progress);
}
item.progress = progress;
item.value = item.offset + progress * item.target;
if (item.modifier) {
item.value = item.modifier(item.value);
}
item.value = clamp(item.value, item.min, item.max);
});
PARALLAX_GROUPS.forEach(({ prop: groupProp }) => {
const groupItems = items.filter((item) => item.group === groupProp);
const styles = groupItems.map(({ value, prop, unit }) => {
if (groupProp === 'opacity') {
return `${value}`;
}
return `${prop}(${value}${unit})`;
});
const styleString = styles.join(' ');
element.style[groupProp] = styleString;
});
}
/** Destroy parallax */
destroy() {
this._observer.disconnect();
if (this._debounceInit) {
clearTimeout(this._debounceInit);
}
}
}
//# sourceMappingURL=index.js.map