baseframe-js
Version:
Baseframe JS is a comprehensive suite of modular plugins and utilities designed for front-end development
189 lines (188 loc) • 6.9 kB
JavaScript
import $be from "base-elem-js";
import Store from "./core/Store";
import { getDataOptions, setParams } from "./util/helpers";
import { debounceResize } from "./fn/debounce";
const VERSION = "2.0.1";
const DATA_NAME = 'Parallax';
const EVENT_NAME = 'parallax';
const DEFAULTS = {
speed: 7,
zSpeed: 5,
cssPrefix: 'parallax',
axis: 'y',
scrollAxis: 'y',
zAxis: false,
relativeElem: false,
bgFill: false,
rootMargin: 0,
scrollMaxPxStop: 5000,
zScrollMaxPxStop: 2000,
minWidth: null,
maxWidth: null
};
export default class Parallax {
params;
zInitOffset;
index;
instanceEvent;
$window;
$element;
element;
elementOffset;
$relElem;
winHeight;
winWidth;
elemHeight;
elemWidth;
speed;
zSpeed;
fillAmount;
bgFill;
bgFillProp;
axis;
zAxis;
scrollMaxPxStop;
zScrollMaxPxStop;
rootMargin;
lastZSpeed;
lastCssInProps;
minWidthIfSet;
maxWidthIfSet;
effectCleared;
cssPrevDir;
static defaults = DEFAULTS;
static version = VERSION;
static pluginName = DATA_NAME;
constructor(element, options, index) {
const s = this;
const dataOptions = getDataOptions(element, EVENT_NAME);
s.$window = $be(window);
s.$element = $be(element);
s.element = element;
s.params = setParams(Parallax.defaults, options, dataOptions);
s.zInitOffset = 0;
s.index = index;
s.instanceEvent = EVENT_NAME + index;
s.$relElem = s.#relElem;
s.cssPrevDir = [];
//props to get updated on resize
s.#updatableProps();
s.#handleEvents();
return s;
}
#handleEvents() {
const s = this;
debounceResize(() => {
s.#updatableProps();
s.parallax(s);
}, `[${s.instanceEvent}_resize]`, true);
$be(window).on([`scroll.${s.instanceEvent}`, `[${s.instanceEvent}]`], () => {
window.requestAnimationFrame(() => {
s.parallax(s);
});
}).trigger(s.instanceEvent);
}
#updatableProps() {
const s = this;
const speed = s.#speed, zSpeed = s.#zSpeed, { cssPrefix } = s.params, speedCss = `${cssPrefix}--${s.params.axis}-${zSpeed > 0 ? 'pos' : 'neg'}`, zSpeedCss = s.params.zAxis ? `${cssPrefix}--z-${zSpeed > 0 ? 'pos' : 'neg'}` : '', newCssDir = zSpeedCss ? [speedCss, zSpeedCss] : speedCss, rm = s.params.rootMargin;
//reset to get new measurements
s.$element.css({
transform: null,
height: null,
width: null
});
if (s.cssPrevDir)
s.$element.rmClass(s.cssPrevDir);
s.$element.addClass(newCssDir);
const relElem = s.$relElem.elem[0];
// const windowRects = s.$window.elemRects();
s.cssPrevDir = newCssDir;
s.winHeight = window.innerHeight;
s.winWidth = window.innerHeight;
s.elemHeight = relElem.scrollHeight;
s.elemWidth = relElem.scrollWidth;
s.speed = speed;
s.zSpeed = zSpeed;
s.fillAmount = s.#fillAmount;
s.bgFill = s.params.bgFill;
s.axis = s.params.axis;
s.bgFillProp = s.axis === 'y' ? 'height' : 'width';
s.zAxis = s.params.zAxis;
s.$relElem = s.#relElem;
s.scrollMaxPxStop = s.params.scrollMaxPxStop;
s.zScrollMaxPxStop = s.params.zScrollMaxPxStop;
s.lastZSpeed = 0;
s.rootMargin = typeof s.params.rootMargin === 'number' ? [rm, rm] : rm;
s.minWidthIfSet = s.params.minWidth ? s.winWidth > s.params.minWidth : true;
s.maxWidthIfSet = s.params.maxWidth ? s.winWidth < s.params.maxWidth : true;
s.elementOffset = s.getElementRects();
if (s.lastCssInProps) {
s.$element.css(s.lastCssInProps);
}
}
getElementRects() {
const s = this;
const elPos = s.$element.elemRects(), top = elPos.top + window.scrollY, left = elPos.left + window.scrollX, bottom = top + s.elemHeight, right = left + s.elemWidth;
return {
top,
left,
bottom,
right
};
}
parallax(s) {
const { scrollAxis } = s.params, [rootMStart, rootMEnd] = s.rootMargin, withinMinAndMaxIfSet = (s.minWidthIfSet && s.maxWidthIfSet), scrollVertical = scrollAxis === 'y', offset = (scrollVertical ? s.elementOffset.top : s.elementOffset.left), winSide = (scrollVertical ? s.winHeight : s.winWidth) - rootMStart, scrollDir = (scrollVertical ? window.scrollY : window.scrollX), pixelStart = (offset > winSide ? (winSide - offset) + scrollDir : offset + (scrollDir - offset));
if (s.isInView(scrollVertical, pixelStart, scrollDir, rootMEnd) && withinMinAndMaxIfSet) {
const speed = (pixelStart * s.speed), zSpeed = (pixelStart * s.zSpeed), speedPx = (speed < s.zScrollMaxPxStop) ? speed : s.zScrollMaxPxStop, zSpeedPx = s.params.zAxis ? ((s.lastZSpeed < s.zScrollMaxPxStop) ? zSpeed : s.lastZSpeed) : 0, translateParams = s.axis === 'y' ? `0, ${speedPx}px, ${zSpeedPx}px` : `${speedPx}px, 0, ${zSpeedPx}px`;
const cssProps = {
transform: `translate3d(${translateParams})`,
[s.bgFillProp]: s.bgFill ? `calc(100% + ${s.fillAmount}px)` : null
};
s.lastZSpeed = zSpeedPx;
s.lastCssInProps = cssProps;
s.$element.css(cssProps);
}
}
isInView(scrollVertical, pixelStart, scrollDir, rootMargin) {
const s = this;
const elemOffsetEnd = scrollVertical ? s.elementOffset.bottom : s.elementOffset.right;
return scrollDir + rootMargin <= elemOffsetEnd && pixelStart >= 0;
}
//getters
get #fillAmount() {
const s = this;
const saY = s.params.scrollAxis === 'y';
const winSide = s.params.axis === 'y' ? s.winHeight : (s.winWidth + (saY ? 0 : s.elemWidth));
return (winSide * Math.abs(s.speed));
}
get #speed() {
const s = this;
return s.params.speed / 100;
}
get #zSpeed() {
const s = this;
return s.params.zSpeed / 100;
}
get #relElem() {
const s = this;
const { relativeElem } = s.params;
return relativeElem ? relativeElem : s.$element;
}
static remove(element, plugin) {
$be(element).each((elem) => {
const s = plugin || Store(elem, DATA_NAME);
$be(window).off([
`scroll.${s.instanceEvent}`,
`resize.${s.instanceEvent}`,
`[${s.instanceEvent}_resize]`,
`[${s.instanceEvent}`
]);
s.$element.css({
'transform': null,
height: null,
width: null
}).rmClass(s.cssPrevDir);
Store(elem, DATA_NAME, null);
});
}
}