UNPKG

react-nice-scroll

Version:

A React library to smooth scroll and scroll-based animations.

622 lines (538 loc) 20.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var React = require('react'); var React__default = _interopDefault(React); var SmoothScrollbar = _interopDefault(require('smooth-scrollbar')); var ScrollTrigger = require('gsap/dist/ScrollTrigger'); var reactHooksGlobalState = require('react-hooks-global-state'); var gsap = require('gsap'); function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } var AllowScrollPlugin = /*#__PURE__*/function (_Scrollbar$ScrollbarP) { _inheritsLoose(AllowScrollPlugin, _Scrollbar$ScrollbarP); function AllowScrollPlugin() { return _Scrollbar$ScrollbarP.apply(this, arguments) || this; } var _proto = AllowScrollPlugin.prototype; _proto.transformDelta = function transformDelta(delta) { return this.options.allow ? delta : { x: 0, y: 0 }; }; return AllowScrollPlugin; }(SmoothScrollbar.ScrollbarPlugin); AllowScrollPlugin.defaultOptions = { allow: true }; AllowScrollPlugin.pluginName = 'allowScroll'; var WillChangePlugin = /*#__PURE__*/function (_Scrollbar$ScrollbarP) { _inheritsLoose(WillChangePlugin, _Scrollbar$ScrollbarP); function WillChangePlugin() { return _Scrollbar$ScrollbarP.apply(this, arguments) || this; } var _proto = WillChangePlugin.prototype; _proto.transformDelta = function transformDelta(delta, _evt) { this.scrollbar.contentEl.style.willChange = 'transform'; return delta; }; _proto.onRender = function onRender(remainMomentum) { if (remainMomentum.x === 0 && remainMomentum.y === 0) { this.scrollbar.contentEl.style.willChange = ''; } }; return WillChangePlugin; }(SmoothScrollbar.ScrollbarPlugin); WillChangePlugin.pluginName = 'willChange'; var _createGlobalState = /*#__PURE__*/reactHooksGlobalState.createGlobalState({ container: undefined, smoothScrollBar: undefined, allowScroll: true }), useGlobalState = _createGlobalState.useGlobalState; // @ts-nocheck var detectTouch = function detectTouch() { if (typeof window !== 'undefined') { return Boolean('ontouchstart' in window || window.navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0 || window.DocumentTouch && document instanceof DocumentTouch); } }; var _excluded = ["children", "damping", "thumbMinSize", "renderByPixels", "alwaysShowTracks", "continuousScrolling", "delegateTo", "activeSmoothScrollOnTouchDevice", "disableSmoothScroll"]; var ScrollContainer = function ScrollContainer(_ref) { var children = _ref.children, _ref$damping = _ref.damping, damping = _ref$damping === void 0 ? 0.075 : _ref$damping, _ref$thumbMinSize = _ref.thumbMinSize, thumbMinSize = _ref$thumbMinSize === void 0 ? 20 : _ref$thumbMinSize, _ref$renderByPixels = _ref.renderByPixels, renderByPixels = _ref$renderByPixels === void 0 ? false : _ref$renderByPixels, _ref$alwaysShowTracks = _ref.alwaysShowTracks, alwaysShowTracks = _ref$alwaysShowTracks === void 0 ? false : _ref$alwaysShowTracks, _ref$continuousScroll = _ref.continuousScrolling, continuousScrolling = _ref$continuousScroll === void 0 ? true : _ref$continuousScroll, delegateTo = _ref.delegateTo, _ref$activeSmoothScro = _ref.activeSmoothScrollOnTouchDevice, activeSmoothScrollOnTouchDevice = _ref$activeSmoothScro === void 0 ? false : _ref$activeSmoothScro, _ref$disableSmoothScr = _ref.disableSmoothScroll, disableSmoothScroll = _ref$disableSmoothScr === void 0 ? false : _ref$disableSmoothScr, props = _objectWithoutPropertiesLoose(_ref, _excluded); var scrollContainer = React.useRef(null); // @ts-ignore: Unreachable code error var _useGlobalState = useGlobalState('container'), setContainer = _useGlobalState[1]; // @ts-ignore: Unreachable code error var _useGlobalState2 = useGlobalState('smoothScrollBar'), setScrollBar = _useGlobalState2[1]; React.useLayoutEffect(function () { if (detectTouch() && !activeSmoothScrollOnTouchDevice || disableSmoothScroll) { if (scrollContainer.current) { scrollContainer.current.style.overflow = 'auto'; scrollContainer.current.style.height = 'auto'; setContainer(document.body); } return; } SmoothScrollbar.use(AllowScrollPlugin, WillChangePlugin); var view = scrollContainer.current; if (view) { var smoothScroll = SmoothScrollbar.init(view, { damping: damping, thumbMinSize: thumbMinSize, renderByPixels: renderByPixels, alwaysShowTracks: alwaysShowTracks, continuousScrolling: continuousScrolling, delegateTo: delegateTo }); if (smoothScroll) setScrollBar(smoothScroll); ScrollTrigger.ScrollTrigger.scrollerProxy(view, { scrollTop: function scrollTop(value) { if (arguments.length && value) { smoothScroll.scrollTop = value; } return smoothScroll.scrollTop; } }); smoothScroll.addListener(ScrollTrigger.ScrollTrigger.update); if (scrollContainer.current) setContainer(scrollContainer.current); } return function () { SmoothScrollbar == null ? void 0 : SmoothScrollbar.destroyAll(); }; }, [alwaysShowTracks, continuousScrolling, damping, renderByPixels, thumbMinSize, setContainer, activeSmoothScrollOnTouchDevice, disableSmoothScroll, delegateTo, setScrollBar]); return React__default.createElement("div", Object.assign({ className: "ns-container" }, props, { ref: scrollContainer }), children); }; var _excluded$1 = ["children", "top", "left", "right"]; var FixedElement = function FixedElement(_ref) { var children = _ref.children, _ref$top = _ref.top, top = _ref$top === void 0 ? 0 : _ref$top, left = _ref.left, right = _ref.right, props = _objectWithoutPropertiesLoose(_ref, _excluded$1); var fixedElement = React.useRef(null); var _useGlobalState = useGlobalState('smoothScrollBar'), smoothScrollBar = _useGlobalState[0]; React.useLayoutEffect(function () { if (smoothScrollBar) smoothScrollBar.addListener(function (_ref2) { var offset = _ref2.offset; if (fixedElement.current) { fixedElement.current.style.top = top + offset.y + 'px'; if (right !== undefined) fixedElement.current.style.right = right + offset.x + 'px'; if (left !== undefined) fixedElement.current.style.left = left + offset.x + 'px'; } }); return function () {}; }, [left, right, smoothScrollBar, top]); return React__default.createElement("div", Object.assign({ className: "ns-fixed-element", ref: fixedElement, style: { top: top, left: left, right: right } }, props), children); }; var gellyAnimation = function gellyAnimation(element, velocity, axis, intensity, min, max, duration, ease) { var proxy = { skew: 0 }, skewSetter = gsap.gsap.quickSetter(element, axis, 'deg'), clamp = gsap.gsap.utils.clamp(min, max); var skew = clamp(velocity / intensity); if (Math.abs(skew) > Math.abs(proxy.skew)) { proxy.skew = skew; } return gsap.gsap.to(proxy, { skew: 0, duration: duration, ease: ease, overwrite: true, onUpdate: function onUpdate() { return skewSetter(proxy.skew); }, immediateRender: false }); }; var parallaxAnimation = function parallaxAnimation(element, trigger, scroller, start, end, axis, fromPercent, toPercent, containerAnimation) { return gsap.gsap.timeline({ scrollTrigger: { trigger: trigger, scroller: scroller, scrub: true, start: start, end: end, pin: false, containerAnimation: containerAnimation } }).from(element, { yPercent: axis === 'y' ? fromPercent : undefined, xPercent: axis === 'x' ? fromPercent : undefined, ease: 'none', immediateRender: scroller === document.body }).to(element, { yPercent: axis === 'y' ? toPercent : undefined, xPercent: axis === 'x' ? toPercent : undefined, ease: 'none' }); }; var _excluded$2 = ["children", "axis", "trigger", "intensity", "min", "max", "duration", "ease", "start", "end"]; gsap.gsap.registerPlugin(ScrollTrigger.ScrollTrigger); var GellyElement = function GellyElement(_ref) { var children = _ref.children, _ref$axis = _ref.axis, axis = _ref$axis === void 0 ? 'skewY' : _ref$axis, trigger = _ref.trigger, _ref$intensity = _ref.intensity, intensity = _ref$intensity === void 0 ? -400 : _ref$intensity, _ref$min = _ref.min, min = _ref$min === void 0 ? -40 : _ref$min, _ref$max = _ref.max, max = _ref$max === void 0 ? 40 : _ref$max, _ref$duration = _ref.duration, duration = _ref$duration === void 0 ? 0.3 : _ref$duration, _ref$ease = _ref.ease, ease = _ref$ease === void 0 ? 'expo.out' : _ref$ease, _ref$start = _ref.start, start = _ref$start === void 0 ? 'top bottom' : _ref$start, _ref$end = _ref.end, end = _ref$end === void 0 ? 'bottom top' : _ref$end, props = _objectWithoutPropertiesLoose(_ref, _excluded$2); var gellyElement = React.useRef(null); var _useGlobalState = useGlobalState('container'), scroller = _useGlobalState[0]; React.useLayoutEffect(function () { var animation; var scrollTrigger = ScrollTrigger.ScrollTrigger.create({ trigger: trigger || gellyElement.current, scroller: scroller, start: start, end: end, onUpdate: function onUpdate(self) { if (gellyElement.current) animation = gellyAnimation(gellyElement.current, self.getVelocity(), axis, intensity, min, max, duration, ease); } }); return function () { var _animation; (_animation = animation) == null ? void 0 : _animation.kill(); scrollTrigger == null ? void 0 : scrollTrigger.kill(); }; }, [axis, duration, ease, end, intensity, max, min, scroller, start, trigger]); return React__default.createElement("div", Object.assign({ className: "ns-gelly-element", ref: gellyElement }, props), children); }; var _excluded$3 = ["children", "toRight", "start", "addAnimation"]; var HorizontalSection = function HorizontalSection(_ref) { var children = _ref.children, _ref$toRight = _ref.toRight, toRight = _ref$toRight === void 0 ? true : _ref$toRight, _ref$start = _ref.start, start = _ref$start === void 0 ? 'top top' : _ref$start, addAnimation = _ref.addAnimation, props = _objectWithoutPropertiesLoose(_ref, _excluded$3); var horizontal = React.useRef(null); var pinWrap = React.useRef(null); var animationWrap = React.useRef(null); var _useGlobalState = useGlobalState('container'), scroller = _useGlobalState[0]; React.useLayoutEffect(function () { var fromTo; if (animationWrap.current) { var scrollWidth = animationWrap.current.scrollWidth; var getToValue = function getToValue() { return -(scrollWidth - window.innerWidth); }; fromTo = gsap.gsap.fromTo(animationWrap.current, { x: function x() { return toRight ? 0 : getToValue(); } }, { x: function x() { return toRight ? getToValue() : 0; }, ease: 'none', scrollTrigger: { trigger: horizontal.current, scroller: scroller, pinType: scroller === document.body ? 'fixed' : 'transform', start: start, end: function end() { return '+=' + scrollWidth; }, onEnter: function onEnter() { if (horizontal.current) horizontal.current.style.willChange = 'transform'; }, onEnterBack: function onEnterBack() { if (horizontal.current) horizontal.current.style.willChange = 'transform'; }, onLeave: function onLeave() { if (horizontal.current) horizontal.current.style.willChange = ''; }, onLeaveBack: function onLeaveBack() { if (horizontal.current) horizontal.current.style.willChange = ''; }, pin: true, invalidateOnRefresh: true, anticipatePin: 1, scrub: true } }); if (addAnimation && fromTo) addAnimation(fromTo); } return function () { var _fromTo; (_fromTo = fromTo) == null ? void 0 : _fromTo.kill(); }; }, [addAnimation, toRight, scroller, start]); return React__default.createElement("section", Object.assign({ className: "ns-horizontal-section", ref: horizontal }, props), React__default.createElement("div", { className: "ns-horizontal-section__pin-wrap", ref: pinWrap }, React__default.createElement("div", { className: "ns-horizontal-section__animation-wrap", ref: animationWrap }, children))); }; var _excluded$4 = ["src", "alt", "triggerElement", "start", "end", "axis", "fromPercent", "toPercent", "containerHeight", "imageScale", "imageObjectPosition"]; var ParallaxImage = function ParallaxImage(_ref) { var src = _ref.src, alt = _ref.alt, triggerElement = _ref.triggerElement, _ref$start = _ref.start, start = _ref$start === void 0 ? 'top bottom' : _ref$start, _ref$end = _ref.end, end = _ref$end === void 0 ? 'bottom top' : _ref$end, _ref$axis = _ref.axis, axis = _ref$axis === void 0 ? 'y' : _ref$axis, _ref$fromPercent = _ref.fromPercent, fromPercent = _ref$fromPercent === void 0 ? -40 : _ref$fromPercent, _ref$toPercent = _ref.toPercent, toPercent = _ref$toPercent === void 0 ? 40 : _ref$toPercent, _ref$containerHeight = _ref.containerHeight, containerHeight = _ref$containerHeight === void 0 ? '100vh' : _ref$containerHeight, _ref$imageScale = _ref.imageScale, imageScale = _ref$imageScale === void 0 ? 1.2 : _ref$imageScale, _ref$imageObjectPosit = _ref.imageObjectPosition, imageObjectPosition = _ref$imageObjectPosit === void 0 ? 'center' : _ref$imageObjectPosit, props = _objectWithoutPropertiesLoose(_ref, _excluded$4); var parallaxImage = React.useRef(null); var parallaxImageInner = React.useRef(null); var _useGlobalState = useGlobalState('container'), scroller = _useGlobalState[0]; React.useLayoutEffect(function () { var trigger = parallaxImage.current || triggerElement; var animation; if (parallaxImageInner.current !== null && trigger && scroller) { animation = parallaxAnimation(parallaxImageInner.current, trigger, scroller, start, end, axis, fromPercent, toPercent); } return function () { var _animation; (_animation = animation) == null ? void 0 : _animation.kill(); }; }, [axis, end, fromPercent, scroller, start, toPercent, triggerElement, containerHeight, imageScale, imageObjectPosition]); return React__default.createElement("figure", Object.assign({ className: "ns-parallax-image", ref: parallaxImage, style: { height: "" + containerHeight } }, props), React__default.createElement("img", { ref: parallaxImageInner, src: src, alt: alt, className: "ns-parallax-image__inner", style: { transform: "scale(" + imageScale + ")", objectPosition: "" + imageObjectPosition }, loading: "lazy", onLoad: function onLoad() { return ScrollTrigger.ScrollTrigger.refresh(); } })); }; var preloadImage = function preloadImage(src) { return new Promise(function (resolve, reject) { var img = new Image(); var xhr = new XMLHttpRequest(); xhr.open('GET', src, true); xhr.responseType = 'blob'; xhr.onload = function () { img.src = URL.createObjectURL(xhr.response); img.onload = function () { return resolve(img); }; }; xhr.onerror = function () { return reject(); }; xhr.send(); }); }; var preloadImages = function preloadImages(urls) { return Promise.all(urls.map(function (src) { return preloadImage(src); })); }; var calcDrawImage = function calcDrawImage(ctx, image, left, top) { if (left === void 0) { left = 0.5; } if (top === void 0) { top = 0.5; } var cWidth = ctx.canvas.width; var cHeight = ctx.canvas.height; var width = image.width; var height = image.height; var ratio = width / height; var cRatio = cWidth / cHeight; var resultHeight, resultWidth; if (ratio > cRatio) { resultHeight = cHeight; resultWidth = cHeight * ratio; } else { resultWidth = cWidth; resultHeight = cWidth / ratio; } ctx.drawImage(image, (cWidth - resultWidth) * left, (cHeight - resultHeight) * top, resultWidth, resultHeight); }; var _excluded$5 = ["imagesPath", "imagesType", "imagesCount", "start", "end"]; var SequenceSection = function SequenceSection(_ref) { var imagesPath = _ref.imagesPath, imagesType = _ref.imagesType, imagesCount = _ref.imagesCount, _ref$start = _ref.start, start = _ref$start === void 0 ? 'top top' : _ref$start, _ref$end = _ref.end, end = _ref$end === void 0 ? '200%' : _ref$end, props = _objectWithoutPropertiesLoose(_ref, _excluded$5); var sequenceSection = React.useRef(null); var sequenceSectionCanvas = React.useRef(null); var _useGlobalState = useGlobalState('container'), scroller = _useGlobalState[0]; React.useLayoutEffect(function () { var urls = []; for (var i = 0; i < imagesCount; i++) { urls.push(imagesPath + "/" + (i + 1) + "." + imagesType); } var images = preloadImages(urls); var ctx; if (sequenceSectionCanvas.current) { ctx = sequenceSectionCanvas.current.getContext('2d'); } var tl = gsap.gsap.timeline({ scrollTrigger: { trigger: sequenceSection.current, scroller: scroller, scrub: true, start: start, end: end, pin: true, pinType: scroller === document.body ? 'fixed' : 'transform', onEnter: function onEnter() { if (sequenceSection.current) sequenceSection.current.style.willChange = 'transform'; }, onEnterBack: function onEnterBack() { if (sequenceSection.current) sequenceSection.current.style.willChange = 'transform'; }, onLeave: function onLeave() { if (sequenceSection.current) sequenceSection.current.style.willChange = ''; }, onLeaveBack: function onLeaveBack() { if (sequenceSection.current) sequenceSection.current.style.willChange = ''; } } }); window.addEventListener('resize', function resize() { ctx.canvas.width = document.documentElement.clientWidth; ctx.canvas.height = document.documentElement.clientHeight; return resize; }()); images.then(function (imgs) { var counter = { i: 0 }; tl.to(counter, { i: imgs.length - 1, roundProps: 'i', ease: 'none', immediateRender: true, onUpdate: function onUpdate() { if (ctx) calcDrawImage(ctx, imgs[counter.i]); } }, 0); window.addEventListener('resize', function () { if (ctx) calcDrawImage(ctx, imgs[counter.i]); }); }); return function () { tl == null ? void 0 : tl.kill(); }; }, [end, imagesCount, imagesPath, imagesType, scroller, start]); return React__default.createElement("section", Object.assign({ className: "ns-sequence-section", ref: sequenceSection }, props), React__default.createElement("canvas", { className: "ns-sequence-section__canvas", ref: sequenceSectionCanvas })); }; exports.FixedElement = FixedElement; exports.GellyElement = GellyElement; exports.HorizontalSection = HorizontalSection; exports.ParallaxImage = ParallaxImage; exports.ScrollContainer = ScrollContainer; exports.SequenceSection = SequenceSection; exports.gellyAnimation = gellyAnimation; exports.parallaxAnimation = parallaxAnimation; exports.useGlobalState = useGlobalState; //# sourceMappingURL=react-nice-scroll.cjs.development.js.map