/*! * simpleParallax - simpleParallax is a simple JavaScript library that gives your website parallax animations on any images, * @date: 07-12-2019 18:53:52, * @version: 5.2.0, * @link: https://simpleparallax.com/ */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define("simpleParallax", [], factory); else if(typeof exports === 'object') exports["simpleParallax"] = factory(); else root["simpleParallax"] = factory(); })(window, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); // CONCATENATED MODULE: ./src/helpers/viewport.js function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var Viewport = /*#__PURE__*/ function () { function Viewport() { _classCallCheck(this, Viewport); this.positions = { top: 0, bottom: 0, height: 0 }; } _createClass(Viewport, [{ key: "setViewportTop", value: function setViewportTop(container) { // if this is a custom container, user the scrollTop this.positions.top = container ? container.scrollTop : window.pageYOffset; return this.positions; } }, { key: "setViewportBottom", value: function setViewportBottom() { this.positions.bottom = this.positions.top + this.positions.height; return this.positions; } }, { key: "setViewportAll", value: function 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; } }]); return Viewport; }(); var viewport = new Viewport(); // CONCATENATED MODULE: ./src/helpers/convertToArray.js // check wether the element is a Node List, a HTML Collection or an array // return an array of nodes var convertToArray = function 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]; }; /* harmony default export */ var helpers_convertToArray = (convertToArray); // CONCATENATED MODULE: ./src/helpers/cssTransform.js // Detect css transform var cssTransform = function cssTransform() { var prefixes = 'transform webkitTransform mozTransform oTransform msTransform'.split(' '); var transform; var i = 0; while (transform === undefined) { transform = document.createElement('div').style[prefixes[i]] !== undefined ? prefixes[i] : undefined; i += 1; } return transform; }; /* harmony default export */ var helpers_cssTransform = (cssTransform()); // CONCATENATED MODULE: ./src/helpers/isImageLoaded.js // check if image is fully loaded var isImageLoaded = function 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; }; /* harmony default export */ var helpers_isImageLoaded = (isImageLoaded); // CONCATENATED MODULE: ./src/instances/parallax.js function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } function parallax_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function parallax_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function parallax_createClass(Constructor, protoProps, staticProps) { if (protoProps) parallax_defineProperties(Constructor.prototype, protoProps); if (staticProps) parallax_defineProperties(Constructor, staticProps); return Constructor; } var parallax_ParallaxInstance = /*#__PURE__*/ function () { function ParallaxInstance(element, options) { parallax_classCallCheck(this, ParallaxInstance); // 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 (helpers_isImageLoaded(element)) { this.init(); } else { this.element.addEventListener('load', this.init); } } parallax_createClass(ParallaxInstance, [{ key: "init", value: function init() { var _this = this; // for some reason, 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(function () { // apply the transition style on the image _this.setTransitionCSS(); }, 10); } // for some reason, 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) }, { key: "wrapElement", value: function wrapElement() { // check is current image is in a tag var elementToWrap = this.element.closest('picture') || this.element; // create a .simpleParallax wrapper container var 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 }, { key: "unWrapElement", value: function unWrapElement() { var wrapper = this.elementContainer; wrapper.replaceWith.apply(wrapper, _toConsumableArray(wrapper.childNodes)); } // apply default style on element }, { key: "setTransformCSS", value: function 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[helpers_cssTransform] = "scale(".concat(this.settings.scale, ")"); } // add will-change CSS property to improve perfomance this.element.style.willChange = 'transform'; } // apply the transition effet }, { key: "setTransitionCSS", value: function setTransitionCSS() { // add transition option this.element.style.transition = "transform ".concat(this.settings.delay, "s ").concat(this.settings.transition); } // remove style of the element }, { key: "unSetStyle", value: function unSetStyle() { // remove will change inline style this.element.style.willChange = ''; this.element.style[helpers_cssTransform] = ''; this.element.style.transition = ''; } // get the current element offset }, { key: "getElementOffset", value: function getElementOffset() { // get position of the element var 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 var 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 }, { key: "buildThresholdList", value: function buildThresholdList() { var thresholds = []; for (var i = 1.0; i <= this.elementHeight; i++) { var ratio = i / this.elementHeight; thresholds.push(ratio); } return thresholds; } // create the Intersection Observer }, { key: "intersectionObserver", value: function intersectionObserver() { var 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 }, { key: "intersectionObserverCallback", value: function intersectionObserverCallback(entries) { for (var 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 }, { key: "checkIfVisible", value: function checkIfVisible() { return this.elementBottom > viewport.positions.top && this.elementTop < viewport.positions.bottom; } // calculate the range between image will be translated }, { key: "getRangeMax", value: function getRangeMax() { // get the real height of the image without scale var 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 }, { key: "getTranslateValue", value: function getTranslateValue() { // calculate the % position of the element comparing to the viewport // rounding percentage to a 1 number float to avoid unn unnecessary calculation var 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 }, { key: "animate", value: function animate() { var translateValueY = 0; var translateValueX = 0; var 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 = "".concat(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 = "".concat(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(".concat(translateValueX, ", ").concat(translateValueY, ", 0) scale(").concat(this.settings.scale, ")"); } else { inlineCss = "translate3d(".concat(translateValueX, ", ").concat(translateValueY, ", 0)"); } // add style on the element using the adequate CSS transform this.element.style[helpers_cssTransform] = inlineCss; } }]); return ParallaxInstance; }(); /* harmony default export */ var parallax = (parallax_ParallaxInstance); // CONCATENATED MODULE: ./src/simpleParallax.js /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return simpleParallax_SimpleParallax; }); function simpleParallax_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function simpleParallax_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function simpleParallax_createClass(Constructor, protoProps, staticProps) { if (protoProps) simpleParallax_defineProperties(Constructor.prototype, protoProps); if (staticProps) simpleParallax_defineProperties(Constructor, staticProps); return Constructor; } var intersectionObserverAvailable = true; var isInit = false; var instances = []; var instancesLength; var frameID; var resizeID; var simpleParallax_SimpleParallax = /*#__PURE__*/ function () { function SimpleParallax(elements, options) { simpleParallax_classCallCheck(this, SimpleParallax); if (!elements) return; this.elements = helpers_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(helpers_convertToArray(this.settings.customContainer)[0]); this.customContainer = helpers_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(); } simpleParallax_createClass(SimpleParallax, [{ key: "init", value: function init() { viewport.setViewportAll(this.customContainer); for (var i = this.elements.length - 1; i >= 0; i--) { var instance = new parallax(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 }, { key: "resizeIsDone", value: function resizeIsDone() { clearTimeout(resizeID); resizeID = setTimeout(this.handleResize, 500); } // handle the resize process, some coordonates need to be re-calculate }, { key: "handleResize", value: function handleResize() { // re-get all the viewport positions viewport.setViewportAll(this.customContainer); for (var 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 }, { key: "proceedRequestAnimationFrame", value: function 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 (var 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 }, { key: "proceedElement", value: function proceedElement(instance) { var 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(); } }, { key: "destroy", value: function destroy() { var _this = this; var instancesToDestroy = []; // remove all instances that need to be destroyed from the instances array instances = instances.filter(function (instance) { if (_this.elements.includes(instance.element)) { // push instance that need to be destroyed into instancesToDestroy instancesToDestroy.push(instance); return false; } return instance; }); for (var 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); } } }]); return SimpleParallax; }(); /***/ }) /******/ ])["default"]; });