react-nice-scroll
Version:
A React library to smooth scroll and scroll-based animations.
622 lines (538 loc) • 20.9 kB
JavaScript
'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