baseframe-js
Version:
A suite of useful Javascript plugins and functions to help with Front-end Development on websites
188 lines (187 loc) • 6.79 kB
JavaScript
import $ from 'cash-dom';
import Store from "./core/Store";
import { getDataOptions } from "./util/helpers";
import throttledResize from "./fn/throttleResize";
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 = $(window);
s.$element = $(element);
s.element = element;
s.params = $.extend({}, 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;
throttledResize(() => {
s.updatableProps();
// $(window).trigger(s.instanceEvent);
s.parallax(s);
}, `${s.instanceEvent}_resize`, true);
$(window).on(`scroll.${s.instanceEvent} ${s.instanceEvent}`, () => {
window.requestAnimationFrame(() => {
s.parallax(s);
});
}).trigger(s.instanceEvent);
}
handleUpdate() {
const s = this;
s.updatableProps();
$(window).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 = `${speedCss}${zSpeedCss}`, rm = s.params.rootMargin;
//reset to get new measurements
s.$element.css({
transform: null,
height: null,
width: null
})
.removeClass(s.cssPrevDir)
.addClass(newCssDir);
s.cssPrevDir = newCssDir;
s.winHeight = s.$window.height();
s.winWidth = s.$window.width();
s.elemHeight = s.$relElem[0].scrollHeight;
s.elemWidth = s.$relElem[0].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.offset(), top = elPos.top, left = elPos.left, 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;
return s.params.relativeElem ?
s.$element.closest(s.params.relativeElem) :
s.$element;
}
static remove(element, plugin) {
$(element).each(function () {
const s = plugin || Store(this, DATA_NAME);
$(window).off(`scroll.${s.instanceEvent} resize.${s.instanceEvent} ${s.instanceEvent}_resize ${s.instanceEvent}`);
s.$element.css({
'transform': null,
height: null,
width: null
}).removeClass(s.cssPrevDir);
Store(this, DATA_NAME, null);
});
}
}