Put simpleParallax and modify logo.php to merge
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
// check wether the element is a Node List, a HTML Collection or an array
|
||||
// return an array of nodes
|
||||
const convertToArray = (elements) => {
|
||||
if (NodeList.prototype.isPrototypeOf(elements) || HTMLCollection.prototype.isPrototypeOf(elements)) return Array.from(elements);
|
||||
if (typeof elements === 'string' || elements instanceof String) return document.querySelectorAll(elements);
|
||||
return [elements];
|
||||
};
|
||||
|
||||
export default convertToArray;
|
||||
@@ -0,0 +1,13 @@
|
||||
// Detect css transform
|
||||
const cssTransform = () => {
|
||||
const prefixes = 'transform webkitTransform mozTransform oTransform msTransform'.split(' ');
|
||||
let transform;
|
||||
let i = 0;
|
||||
while (transform === undefined) {
|
||||
transform = document.createElement('div').style[prefixes[i]] !== undefined ? prefixes[i] : undefined;
|
||||
i += 1;
|
||||
}
|
||||
return transform;
|
||||
};
|
||||
|
||||
export default cssTransform();
|
||||
@@ -0,0 +1,21 @@
|
||||
// check if image is fully loaded
|
||||
const isImageLoaded = (image) => {
|
||||
// check if image is set as the parameter
|
||||
if (!image) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if image has been 100% loaded
|
||||
if (!image.complete) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the image is displayed
|
||||
if (typeof image.naturalWidth !== 'undefined' && image.naturalWidth === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export default isImageLoaded;
|
||||
@@ -0,0 +1,33 @@
|
||||
class Viewport {
|
||||
constructor() {
|
||||
this.positions = {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
height: 0,
|
||||
};
|
||||
}
|
||||
|
||||
setViewportTop(container) {
|
||||
// if this is a custom container, user the scrollTop
|
||||
this.positions.top = (container ? container.scrollTop : window.pageYOffset);
|
||||
return this.positions;
|
||||
}
|
||||
|
||||
setViewportBottom() {
|
||||
this.positions.bottom = this.positions.top + this.positions.height;
|
||||
return this.positions;
|
||||
}
|
||||
|
||||
setViewportAll(container) {
|
||||
// if this is a custom container, user the scrollTop
|
||||
this.positions.top = (container ? container.scrollTop : window.pageYOffset);
|
||||
// if this is a custom container, get the height from the custom container itself
|
||||
this.positions.height = (container ? container.clientHeight : document.documentElement.clientHeight);
|
||||
this.positions.bottom = this.positions.top + this.positions.height;
|
||||
|
||||
return this.positions;
|
||||
}
|
||||
}
|
||||
|
||||
export const viewport = new Viewport();
|
||||
export { viewport as default };
|
||||
249
js/simpleParallaxe/simpleParallax-master/src/instances/parallax.js
Executable file
249
js/simpleParallaxe/simpleParallax-master/src/instances/parallax.js
Executable file
@@ -0,0 +1,249 @@
|
||||
import cssTransform from '../helpers/cssTransform';
|
||||
import isImageLoaded from '../helpers/isImageLoaded';
|
||||
import { viewport } from '../helpers/viewport';
|
||||
|
||||
class ParallaxInstance {
|
||||
constructor(element, options) {
|
||||
// set the element & settings
|
||||
this.element = element;
|
||||
this.elementContainer = element;
|
||||
this.settings = options;
|
||||
this.isVisible = true;
|
||||
this.isInit = false;
|
||||
this.oldTranslateValue = -1;
|
||||
|
||||
this.init = this.init.bind(this);
|
||||
|
||||
// check if images has not been loaded yet
|
||||
if (isImageLoaded(element)) {
|
||||
this.init();
|
||||
} else {
|
||||
this.element.addEventListener('load', this.init);
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
// for some reason, <picture> are init an infinite time on windows OS
|
||||
if (this.isInit) return;
|
||||
|
||||
// check if element has not been already initialized with simpleParallax
|
||||
if (this.element.closest('.simpleParallax')) return;
|
||||
|
||||
if (this.settings.overflow === false) {
|
||||
// if overflow option is set to false
|
||||
// wrap the element into a div to apply overflow
|
||||
this.wrapElement(this.element);
|
||||
}
|
||||
|
||||
// apply the transform style on the image
|
||||
this.setTransformCSS();
|
||||
|
||||
// get the current element offset
|
||||
this.getElementOffset();
|
||||
|
||||
// init the Intesection Observer
|
||||
this.intersectionObserver();
|
||||
|
||||
// get its translated value
|
||||
this.getTranslateValue();
|
||||
|
||||
// apply its translation even if not visible for the first init
|
||||
this.animate();
|
||||
|
||||
// if a delay has been set
|
||||
if (this.settings.delay > 0) {
|
||||
// apply a timeout to avoid buggy effect
|
||||
setTimeout(() => {
|
||||
// apply the transition style on the image
|
||||
this.setTransitionCSS();
|
||||
}, 10);
|
||||
}
|
||||
|
||||
// for some reason, <picture> are init an infinite time on windows OS
|
||||
this.isInit = true;
|
||||
}
|
||||
|
||||
// if overflow option is set to false
|
||||
// wrap the element into a .simpleParallax div and apply overflow hidden to hide the image excedant (result of the scale)
|
||||
wrapElement() {
|
||||
// check is current image is in a <picture> tag
|
||||
const elementToWrap = this.element.closest('picture') || this.element;
|
||||
|
||||
// create a .simpleParallax wrapper container
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.classList.add('simpleParallax');
|
||||
wrapper.style.overflow = 'hidden';
|
||||
|
||||
// append the image inside the new wrapper
|
||||
elementToWrap.parentNode.insertBefore(wrapper, elementToWrap);
|
||||
wrapper.appendChild(elementToWrap);
|
||||
|
||||
this.elementContainer = wrapper;
|
||||
}
|
||||
|
||||
// unwrap the element from .simpleParallax wrapper container
|
||||
unWrapElement() {
|
||||
const wrapper = this.elementContainer;
|
||||
wrapper.replaceWith(...wrapper.childNodes);
|
||||
}
|
||||
|
||||
// apply default style on element
|
||||
setTransformCSS() {
|
||||
if (this.settings.overflow === false) {
|
||||
// if overflow option is set to false
|
||||
// add scale style so the image can be translated without getting out of its container
|
||||
this.element.style[cssTransform] = `scale(${this.settings.scale})`;
|
||||
}
|
||||
|
||||
// add will-change CSS property to improve perfomance
|
||||
this.element.style.willChange = 'transform';
|
||||
}
|
||||
|
||||
// apply the transition effet
|
||||
setTransitionCSS() {
|
||||
// add transition option
|
||||
this.element.style.transition = `transform ${this.settings.delay}s ${this.settings.transition}`;
|
||||
}
|
||||
|
||||
// remove style of the element
|
||||
unSetStyle() {
|
||||
// remove will change inline style
|
||||
this.element.style.willChange = '';
|
||||
this.element.style[cssTransform] = '';
|
||||
this.element.style.transition = '';
|
||||
}
|
||||
|
||||
// get the current element offset
|
||||
getElementOffset() {
|
||||
// get position of the element
|
||||
const positions = this.elementContainer.getBoundingClientRect();
|
||||
// get height
|
||||
this.elementHeight = positions.height;
|
||||
// get offset top
|
||||
this.elementTop = positions.top + viewport.positions.top;
|
||||
// if there is a custom container
|
||||
if (this.settings.customContainer) {
|
||||
// we need to do some calculation to get the position from the parent rather than the viewport
|
||||
const parentPositions = this.settings.customContainer.getBoundingClientRect();
|
||||
this.elementTop = (positions.top - parentPositions.top) + viewport.positions.top;
|
||||
}
|
||||
// get offset bottom
|
||||
this.elementBottom = this.elementHeight + this.elementTop;
|
||||
}
|
||||
|
||||
// build the Threshold array to cater change for every pixel scrolled
|
||||
buildThresholdList() {
|
||||
const thresholds = [];
|
||||
for (let i = 1.0; i <= this.elementHeight; i++) {
|
||||
const ratio = i / this.elementHeight;
|
||||
thresholds.push(ratio);
|
||||
}
|
||||
return thresholds;
|
||||
}
|
||||
|
||||
// create the Intersection Observer
|
||||
intersectionObserver() {
|
||||
const options = {
|
||||
root: null,
|
||||
threshold: this.buildThresholdList(),
|
||||
};
|
||||
this.observer = new IntersectionObserver(this.intersectionObserverCallback.bind(this), options);
|
||||
this.observer.observe(this.element);
|
||||
}
|
||||
|
||||
// Intersection Observer Callback to set the element at visible state or not
|
||||
intersectionObserverCallback(entries) {
|
||||
for (let i = entries.length - 1; i >= 0; i--) {
|
||||
if (entries[i].isIntersecting) {
|
||||
this.isVisible = true;
|
||||
} else {
|
||||
this.isVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if the current element is visible in the Viewport
|
||||
// for browser that not support Intersection Observer API
|
||||
checkIfVisible() {
|
||||
return this.elementBottom > viewport.positions.top && this.elementTop < viewport.positions.bottom;
|
||||
}
|
||||
|
||||
// calculate the range between image will be translated
|
||||
getRangeMax() {
|
||||
// get the real height of the image without scale
|
||||
const elementImageHeight = this.element.clientHeight;
|
||||
|
||||
// range is calculate with the image height by the scale
|
||||
this.rangeMax = elementImageHeight * this.settings.scale - elementImageHeight;
|
||||
}
|
||||
|
||||
// get the percentage and the translate value to apply on the element
|
||||
getTranslateValue() {
|
||||
// calculate the % position of the element comparing to the viewport
|
||||
// rounding percentage to a 1 number float to avoid unn unnecessary calculation
|
||||
let percentage = ((viewport.positions.bottom - this.elementTop) / ((viewport.positions.height + this.elementHeight) / 100)).toFixed(1);
|
||||
|
||||
// sometime the percentage exceeds 100 or goes below 0
|
||||
percentage = Math.min(100, Math.max(0, percentage));
|
||||
|
||||
// sometime the same percentage is returned
|
||||
// if so we don't do aything
|
||||
if (this.oldPercentage === percentage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if not range max is set, recalculate it
|
||||
if (!this.rangeMax) {
|
||||
this.getRangeMax();
|
||||
}
|
||||
|
||||
// transform this % into the max range of the element
|
||||
// rounding translateValue to a non float int - as minimum pixel for browser to render is 1 (no 0.5)
|
||||
this.translateValue = ((percentage / 100) * this.rangeMax - this.rangeMax / 2).toFixed(0);
|
||||
|
||||
// sometime the same translate value is returned
|
||||
// if so we don't do aything
|
||||
if (this.oldTranslateValue === this.translateValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// store the current percentage
|
||||
this.oldPercentage = percentage;
|
||||
this.oldTranslateValue = this.translateValue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// animate the image
|
||||
animate() {
|
||||
let translateValueY = 0;
|
||||
let translateValueX = 0;
|
||||
let inlineCss;
|
||||
|
||||
if (this.settings.orientation.includes('left') || this.settings.orientation.includes('right')) {
|
||||
// if orientation option is left or right
|
||||
// use horizontal axe - X axe
|
||||
translateValueX = `${this.settings.orientation.includes('left') ? this.translateValue * -1 : this.translateValue}px`;
|
||||
}
|
||||
|
||||
if (this.settings.orientation.includes('up') || this.settings.orientation.includes('down')) {
|
||||
// if orientation option is up or down
|
||||
// use vertical axe - Y axe
|
||||
translateValueY = `${this.settings.orientation.includes('up') ? this.translateValue * -1 : this.translateValue}px`;
|
||||
}
|
||||
|
||||
// set style to apply to the element
|
||||
if (this.settings.overflow === false) {
|
||||
// if overflow option is set to false
|
||||
// add the scale style
|
||||
inlineCss = `translate3d(${translateValueX}, ${translateValueY}, 0) scale(${this.settings.scale})`;
|
||||
} else {
|
||||
inlineCss = `translate3d(${translateValueX}, ${translateValueY}, 0)`;
|
||||
}
|
||||
|
||||
// add style on the element using the adequate CSS transform
|
||||
this.element.style[cssTransform] = inlineCss;
|
||||
}
|
||||
}
|
||||
|
||||
export default ParallaxInstance;
|
||||
181
js/simpleParallaxe/simpleParallax-master/src/simpleParallax.js
Executable file
181
js/simpleParallaxe/simpleParallax-master/src/simpleParallax.js
Executable file
@@ -0,0 +1,181 @@
|
||||
import { viewport } from './helpers/viewport';
|
||||
import convertToArray from './helpers/convertToArray';
|
||||
|
||||
import ParallaxInstance from './instances/parallax';
|
||||
|
||||
let intersectionObserverAvailable = true;
|
||||
let isInit = false;
|
||||
let instances = [];
|
||||
let instancesLength;
|
||||
let frameID;
|
||||
let resizeID;
|
||||
|
||||
export default class SimpleParallax {
|
||||
constructor(elements, options) {
|
||||
if (!elements) return;
|
||||
this.elements = convertToArray(elements);
|
||||
this.defaults = {
|
||||
delay: 0.4,
|
||||
orientation: 'up',
|
||||
scale: 1.3,
|
||||
overflow: false,
|
||||
transition: 'cubic-bezier(0,0,0,1)',
|
||||
customContainer: false,
|
||||
};
|
||||
|
||||
this.settings = Object.assign(this.defaults, options);
|
||||
|
||||
// check if the browser handle the Intersection Observer API
|
||||
if (!('IntersectionObserver' in window)) intersectionObserverAvailable = false;
|
||||
|
||||
if (this.settings.customContainer) {
|
||||
console.log(convertToArray(this.settings.customContainer)[0])
|
||||
this.customContainer = convertToArray(this.settings.customContainer)[0];
|
||||
}
|
||||
|
||||
this.lastPosition = -1;
|
||||
|
||||
this.resizeIsDone = this.resizeIsDone.bind(this);
|
||||
this.handleResize = this.handleResize.bind(this);
|
||||
this.proceedRequestAnimationFrame = this.proceedRequestAnimationFrame.bind(this);
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
viewport.setViewportAll(this.customContainer);
|
||||
|
||||
for (let i = this.elements.length - 1; i >= 0; i--) {
|
||||
const instance = new ParallaxInstance(this.elements[i], this.settings);
|
||||
instances.push(instance);
|
||||
}
|
||||
|
||||
// update the instance length
|
||||
instancesLength = instances.length;
|
||||
|
||||
// only if this is the first simpleParallax init
|
||||
if (!isInit) {
|
||||
// init the frame
|
||||
this.proceedRequestAnimationFrame();
|
||||
|
||||
window.addEventListener('resize', this.resizeIsDone);
|
||||
|
||||
isInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
// wait for resize to be completely done
|
||||
resizeIsDone() {
|
||||
clearTimeout(resizeID);
|
||||
resizeID = setTimeout(this.handleResize, 500);
|
||||
}
|
||||
|
||||
// handle the resize process, some coordonates need to be re-calculate
|
||||
handleResize() {
|
||||
// re-get all the viewport positions
|
||||
viewport.setViewportAll(this.customContainer);
|
||||
|
||||
for (let i = instancesLength - 1; i >= 0; i--) {
|
||||
// re-get the current element offset
|
||||
instances[i].getElementOffset();
|
||||
|
||||
// re-get the range if the current element
|
||||
instances[i].getRangeMax();
|
||||
}
|
||||
|
||||
// force the request animation frame to fired
|
||||
this.lastPosition = -1;
|
||||
}
|
||||
|
||||
// animation frame
|
||||
proceedRequestAnimationFrame() {
|
||||
// get the offset top of the viewport
|
||||
viewport.setViewportTop(this.customContainer);
|
||||
|
||||
if (this.lastPosition === viewport.positions.top) {
|
||||
// if last position if the same than the curent one
|
||||
// callback the animationFrame and exit the current loop
|
||||
frameID = window.requestAnimationFrame(this.proceedRequestAnimationFrame);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// get the offset bottom of the viewport
|
||||
viewport.setViewportBottom();
|
||||
|
||||
// proceed with the current element
|
||||
for (let i = instancesLength - 1; i >= 0; i--) {
|
||||
this.proceedElement(instances[i]);
|
||||
}
|
||||
|
||||
// callback the animationFrame
|
||||
frameID = window.requestAnimationFrame(this.proceedRequestAnimationFrame);
|
||||
|
||||
// store the last position
|
||||
this.lastPosition = viewport.positions.top;
|
||||
}
|
||||
|
||||
// proceed the element
|
||||
proceedElement(instance) {
|
||||
let isVisible = false;
|
||||
|
||||
// is not support for Intersection Observer API
|
||||
// or if this is a custom container
|
||||
// use old function to check if element visible
|
||||
if (!intersectionObserverAvailable || this.customContainer) {
|
||||
isVisible = instance.checkIfVisible();
|
||||
// if support
|
||||
// use response from Intersection Observer API Callback
|
||||
} else {
|
||||
isVisible = instance.isVisible;
|
||||
}
|
||||
|
||||
// if element not visible, stop it
|
||||
if (!isVisible) return;
|
||||
|
||||
// if percentage is equal to the last one, no need to continue
|
||||
if (!instance.getTranslateValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// animate the image
|
||||
instance.animate();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
const instancesToDestroy = [];
|
||||
|
||||
// remove all instances that need to be destroyed from the instances array
|
||||
instances = instances.filter((instance) => {
|
||||
if (this.elements.includes(instance.element)) {
|
||||
// push instance that need to be destroyed into instancesToDestroy
|
||||
instancesToDestroy.push(instance);
|
||||
return false;
|
||||
}
|
||||
return instance;
|
||||
});
|
||||
|
||||
for (let i = instancesToDestroy.length - 1; i >= 0; i--) {
|
||||
// unset style
|
||||
instancesToDestroy[i].unSetStyle();
|
||||
|
||||
if (this.settings.overflow === false) {
|
||||
// if overflow option is set to false
|
||||
// unwrap the element from .simpleParallax wrapper container
|
||||
instancesToDestroy[i].unWrapElement();
|
||||
}
|
||||
}
|
||||
|
||||
// update the instance length var
|
||||
instancesLength = instances.length;
|
||||
|
||||
// if no instances left, remove the raf and resize event = simpleParallax fully destroyed
|
||||
if (!instancesLength) {
|
||||
// cancel the animation frame
|
||||
window.cancelAnimationFrame(frameID);
|
||||
|
||||
// detach the resize event
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user