use-scroller 1.2.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +390 -95
- package/dist/chunk-IFT3KMZJ.js +1203 -0
- package/dist/core.d.ts +123 -0
- package/dist/core.js +38 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +348 -0
- package/package.json +38 -24
- package/dist/cjs/hooks/use-iso-morphic-effect.d.ts +0 -2
- package/dist/cjs/hooks/use-iso-morphic-effect.js +0 -6
- package/dist/cjs/hooks/use-scroll.d.ts +0 -24
- package/dist/cjs/hooks/use-scroll.js +0 -80
- package/dist/cjs/hooks/use-window-scroll.d.ts +0 -13
- package/dist/cjs/hooks/use-window-scroll.js +0 -62
- package/dist/cjs/index.d.ts +0 -3
- package/dist/cjs/index.js +0 -19
- package/dist/cjs/scroller.d.ts +0 -10
- package/dist/cjs/scroller.js +0 -94
- package/dist/cjs/types.d.ts +0 -17
- package/dist/cjs/types.js +0 -2
- package/dist/cjs/utils.d.ts +0 -37
- package/dist/cjs/utils.js +0 -92
- package/dist/esm/hooks/use-iso-morphic-effect.d.ts +0 -2
- package/dist/esm/hooks/use-iso-morphic-effect.js +0 -3
- package/dist/esm/hooks/use-scroll.d.ts +0 -24
- package/dist/esm/hooks/use-scroll.js +0 -76
- package/dist/esm/hooks/use-window-scroll.d.ts +0 -13
- package/dist/esm/hooks/use-window-scroll.js +0 -58
- package/dist/esm/index.d.ts +0 -3
- package/dist/esm/index.js +0 -3
- package/dist/esm/scroller.d.ts +0 -10
- package/dist/esm/scroller.js +0 -90
- package/dist/esm/types.d.ts +0 -17
- package/dist/esm/types.js +0 -1
- package/dist/esm/utils.d.ts +0 -37
- package/dist/esm/utils.js +0 -85
- package/src/hooks/use-iso-morphic-effect.ts +0 -4
- package/src/hooks/use-scroll.ts +0 -84
- package/src/hooks/use-window-scroll.ts +0 -73
- package/src/index.ts +0 -3
- package/src/scroller.ts +0 -107
- package/src/types.ts +0 -21
- package/src/utils.ts +0 -103
package/dist/esm/scroller.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { defaultScrollOptions, getEasing, resolveScrollValues, validateElement } from './utils';
|
|
2
|
-
export function createScroller(container, options) {
|
|
3
|
-
if (options === void 0) { options = defaultScrollOptions; }
|
|
4
|
-
validateElement(container);
|
|
5
|
-
var duration = options.duration, easingOption = options.easingOption, direction = options.direction;
|
|
6
|
-
var _a = resolveScrollValues(container), scrollLeft = _a.scrollLeft, scrollTop = _a.scrollTop, clientWidth = _a.clientWidth, clientHeight = _a.clientHeight, scrollWidth = _a.scrollWidth, scrollHeight = _a.scrollHeight;
|
|
7
|
-
function scroll(from, to, layout) {
|
|
8
|
-
if (layout === void 0) { layout = direction; }
|
|
9
|
-
var startTime = null;
|
|
10
|
-
var requestId = 0;
|
|
11
|
-
var distance = Math.abs(to - from);
|
|
12
|
-
function calculateCurrentPosition(timeFraction) {
|
|
13
|
-
var easingFunc = getEasing(easingOption);
|
|
14
|
-
return easingFunc(timeFraction) * distance + from;
|
|
15
|
-
}
|
|
16
|
-
function updateScrollPosition(currentPosition) {
|
|
17
|
-
container.scrollTo({
|
|
18
|
-
left: layout === 'horizontal' ? currentPosition : undefined,
|
|
19
|
-
top: layout === 'vertical' ? currentPosition : undefined,
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
function animateScroll(currentTime) {
|
|
23
|
-
if (!startTime)
|
|
24
|
-
startTime = currentTime;
|
|
25
|
-
var elapsedTime = currentTime - startTime;
|
|
26
|
-
var timeFraction = elapsedTime / duration;
|
|
27
|
-
var currentPosition = calculateCurrentPosition(timeFraction);
|
|
28
|
-
updateScrollPosition(currentPosition);
|
|
29
|
-
if (timeFraction < 1) {
|
|
30
|
-
requestId = requestAnimationFrame(animateScroll);
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
cancelAnimationFrame(requestId);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
requestId = requestAnimationFrame(animateScroll);
|
|
37
|
-
}
|
|
38
|
-
function scrollTo(to) {
|
|
39
|
-
var from = direction === 'vertical' ? scrollTop : scrollLeft;
|
|
40
|
-
scroll(from, to);
|
|
41
|
-
}
|
|
42
|
-
function scrollToCenter() {
|
|
43
|
-
var from = 0;
|
|
44
|
-
var to = 0;
|
|
45
|
-
if (direction === 'vertical') {
|
|
46
|
-
var scrollYCenterPosition = (scrollHeight - clientHeight) / 2;
|
|
47
|
-
from = scrollTop;
|
|
48
|
-
to = scrollYCenterPosition;
|
|
49
|
-
}
|
|
50
|
-
if (direction === 'horizontal') {
|
|
51
|
-
var scrollXCenterPosition = (scrollWidth - clientWidth) / 2;
|
|
52
|
-
from = scrollLeft;
|
|
53
|
-
to = scrollXCenterPosition;
|
|
54
|
-
}
|
|
55
|
-
scroll(from, to, direction);
|
|
56
|
-
}
|
|
57
|
-
function scrollToTarget(target, offset) {
|
|
58
|
-
if (offset === void 0) { offset = 0; }
|
|
59
|
-
validateElement(target);
|
|
60
|
-
var offsetTop = target.offsetTop, offsetLeft = target.offsetLeft;
|
|
61
|
-
var from = 0;
|
|
62
|
-
var to = 0;
|
|
63
|
-
if (direction === 'vertical') {
|
|
64
|
-
from = scrollTop;
|
|
65
|
-
to = offsetTop - offset;
|
|
66
|
-
}
|
|
67
|
-
if (direction === 'horizontal') {
|
|
68
|
-
from = scrollLeft;
|
|
69
|
-
to = offsetLeft - offset;
|
|
70
|
-
}
|
|
71
|
-
scroll(from, to, direction);
|
|
72
|
-
}
|
|
73
|
-
return {
|
|
74
|
-
scrollTo: scrollTo,
|
|
75
|
-
scrollToCenter: scrollToCenter,
|
|
76
|
-
scrollToTarget: scrollToTarget,
|
|
77
|
-
scrollToLeft: function (offset) {
|
|
78
|
-
scroll(scrollLeft, offset || scrollLeft - clientWidth, 'horizontal');
|
|
79
|
-
},
|
|
80
|
-
scrollToRight: function (offset) {
|
|
81
|
-
scroll(scrollLeft, offset || scrollLeft + clientWidth, 'horizontal');
|
|
82
|
-
},
|
|
83
|
-
scrollToTop: function (offset) {
|
|
84
|
-
scroll(scrollTop, offset || scrollTop - clientHeight, 'vertical');
|
|
85
|
-
},
|
|
86
|
-
scrollToBottom: function (offset) {
|
|
87
|
-
scroll(scrollTop, offset || scrollTop + clientHeight, 'vertical');
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
}
|
package/dist/esm/types.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export type ScrollOptions = {
|
|
2
|
-
direction: 'horizontal' | 'vertical';
|
|
3
|
-
duration: number;
|
|
4
|
-
easingOption: EasingOptions;
|
|
5
|
-
};
|
|
6
|
-
export type ScrollerProps = {
|
|
7
|
-
container: HTMLElement | Window;
|
|
8
|
-
options: ScrollOptions;
|
|
9
|
-
};
|
|
10
|
-
export type EasingOptions = 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
|
|
11
|
-
export type EasingFunction = (t: number) => number;
|
|
12
|
-
export interface EasingFunctions {
|
|
13
|
-
linear: EasingFunction;
|
|
14
|
-
'ease-in': EasingFunction;
|
|
15
|
-
'ease-out': EasingFunction;
|
|
16
|
-
'ease-in-out': EasingFunction;
|
|
17
|
-
}
|
package/dist/esm/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/esm/utils.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { RefObject } from 'react';
|
|
2
|
-
import { EasingFunction, EasingFunctions, EasingOptions, ScrollOptions } from './types';
|
|
3
|
-
export declare const isServer: boolean;
|
|
4
|
-
export declare const defaultScrollOptions: ScrollOptions;
|
|
5
|
-
/**
|
|
6
|
-
* Resolves scroll values based on the type of element.
|
|
7
|
-
* @param {HTMLElement | Window} element - The element to retrieve scroll values from.
|
|
8
|
-
* @returns {object} - An object containing scroll-related properties.
|
|
9
|
-
*/
|
|
10
|
-
export declare const resolveScrollValues: (element: HTMLElement | Window) => {
|
|
11
|
-
scrollLeft: number;
|
|
12
|
-
scrollTop: number;
|
|
13
|
-
scrollWidth: number;
|
|
14
|
-
clientWidth: number;
|
|
15
|
-
clientHeight: number;
|
|
16
|
-
scrollHeight: number;
|
|
17
|
-
scrollLeftMax: number;
|
|
18
|
-
scrollTopMax: number;
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Validate if the element passed to the function is valid.
|
|
22
|
-
* @param {HTMLElement | Window} element - The element to validate.
|
|
23
|
-
*/
|
|
24
|
-
export declare function validateElement(element?: HTMLElement | Window): void;
|
|
25
|
-
/**
|
|
26
|
-
* Get the DOM element from a React ref object.
|
|
27
|
-
* @param {RefObject<HTMLElement>} ref - The React ref object.
|
|
28
|
-
* @returns {HTMLElement} - The DOM element.
|
|
29
|
-
*/
|
|
30
|
-
export declare const getElement: (ref: RefObject<HTMLElement>) => HTMLElement;
|
|
31
|
-
export declare const easingMap: EasingFunctions;
|
|
32
|
-
/**
|
|
33
|
-
* Get an easing function based on the provided easing option or the default option.
|
|
34
|
-
* @param {EasingOptions} easing - The easing option.
|
|
35
|
-
* @returns {EasingFunction} - The easing function.
|
|
36
|
-
*/
|
|
37
|
-
export declare const getEasing: (easing?: EasingOptions) => EasingFunction;
|
package/dist/esm/utils.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
// Check if the code is running on the server-side.
|
|
2
|
-
export var isServer = typeof window === 'undefined' || typeof document === 'undefined';
|
|
3
|
-
// Default scroll options used if no options are provided.
|
|
4
|
-
export var defaultScrollOptions = {
|
|
5
|
-
direction: 'vertical',
|
|
6
|
-
duration: 300,
|
|
7
|
-
easingOption: 'ease-in-out',
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Resolves scroll values based on the type of element.
|
|
11
|
-
* @param {HTMLElement | Window} element - The element to retrieve scroll values from.
|
|
12
|
-
* @returns {object} - An object containing scroll-related properties.
|
|
13
|
-
*/
|
|
14
|
-
export var resolveScrollValues = function (element) {
|
|
15
|
-
var isWindow = element instanceof Window;
|
|
16
|
-
var docElement = document.documentElement;
|
|
17
|
-
var docBody = document.body;
|
|
18
|
-
var scrollLeftMax = isWindow
|
|
19
|
-
? Math.max(docBody.scrollLeft, docElement.scrollLeft)
|
|
20
|
-
: element.scrollWidth - element.clientWidth;
|
|
21
|
-
var scrollTopMax = isWindow
|
|
22
|
-
? Math.max(docBody.scrollTop, docElement.scrollTop)
|
|
23
|
-
: element.scrollHeight - element.clientHeight;
|
|
24
|
-
return {
|
|
25
|
-
scrollLeft: isWindow ? docBody.scrollLeft || docElement.scrollLeft : element.scrollLeft,
|
|
26
|
-
scrollTop: isWindow ? docBody.scrollTop || docElement.scrollTop : element.scrollTop,
|
|
27
|
-
scrollWidth: isWindow ? docBody.scrollWidth || docElement.scrollWidth : element.scrollWidth,
|
|
28
|
-
clientWidth: isWindow ? docBody.clientWidth || docElement.clientWidth : element.clientWidth,
|
|
29
|
-
clientHeight: isWindow ? docBody.clientHeight || docElement.clientHeight : element.clientHeight,
|
|
30
|
-
scrollHeight: isWindow ? docBody.scrollHeight || docElement.scrollHeight : element.scrollHeight,
|
|
31
|
-
scrollLeftMax: scrollLeftMax,
|
|
32
|
-
scrollTopMax: scrollTopMax,
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* Validate if the element passed to the function is valid.
|
|
37
|
-
* @param {HTMLElement | Window} element - The element to validate.
|
|
38
|
-
*/
|
|
39
|
-
export function validateElement(element) {
|
|
40
|
-
if (element === undefined) {
|
|
41
|
-
throw new Error("The element passed to scroller() was undefined");
|
|
42
|
-
}
|
|
43
|
-
if (!(element instanceof HTMLElement || Window)) {
|
|
44
|
-
throw new TypeError("\n The element passed to scroller() must be a valid element. \n You passed ".concat(element, ".\n "));
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Get the DOM element from a React ref object.
|
|
49
|
-
* @param {RefObject<HTMLElement>} ref - The React ref object.
|
|
50
|
-
* @returns {HTMLElement} - The DOM element.
|
|
51
|
-
*/
|
|
52
|
-
export var getElement = function (ref) {
|
|
53
|
-
if (!ref.current) {
|
|
54
|
-
throw new Error("Could not resolve ref object: ".concat(ref.current));
|
|
55
|
-
}
|
|
56
|
-
return ref.current;
|
|
57
|
-
};
|
|
58
|
-
// Map of easing functions for scroll animations.
|
|
59
|
-
export var easingMap = {
|
|
60
|
-
linear: function (t) {
|
|
61
|
-
return t;
|
|
62
|
-
},
|
|
63
|
-
'ease-in': function (t) {
|
|
64
|
-
return t * t;
|
|
65
|
-
},
|
|
66
|
-
'ease-out': function (t) {
|
|
67
|
-
return t * (2 - t);
|
|
68
|
-
},
|
|
69
|
-
'ease-in-out': function (t) {
|
|
70
|
-
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
/**
|
|
74
|
-
* Get an easing function based on the provided easing option or the default option.
|
|
75
|
-
* @param {EasingOptions} easing - The easing option.
|
|
76
|
-
* @returns {EasingFunction} - The easing function.
|
|
77
|
-
*/
|
|
78
|
-
export var getEasing = function (easing) {
|
|
79
|
-
var easeFunc = easingMap[easing || defaultScrollOptions.easingOption];
|
|
80
|
-
if (!easeFunc) {
|
|
81
|
-
var options = Object.keys(easingMap).join(',');
|
|
82
|
-
throw new Error("Scroll error: scroller does not support an easing option of \"".concat(easing, "\". Supported options are ").concat(options));
|
|
83
|
-
}
|
|
84
|
-
return easeFunc;
|
|
85
|
-
};
|
package/src/hooks/use-scroll.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { useRef, useState } from 'react'
|
|
2
|
-
import { createScroller } from '../scroller'
|
|
3
|
-
import { ScrollOptions } from '../types'
|
|
4
|
-
import { defaultScrollOptions, getElement } from '../utils'
|
|
5
|
-
import { useIsoMorphicEffect } from './use-iso-morphic-effect'
|
|
6
|
-
|
|
7
|
-
const initialState = {
|
|
8
|
-
left: 0, // Current scroll position on the X-axis
|
|
9
|
-
top: 0, // Current scroll position on the Y-axis
|
|
10
|
-
isScrollable: false, // Determines if the container has content that can be scrolled
|
|
11
|
-
isScrolledLeft: true, // Determines if the container is at its leftmost position
|
|
12
|
-
isScrolledRight: false, // Determines if the container is at its rightmost position
|
|
13
|
-
isScrolledTop: true, // Determines if the container is at its topmost position
|
|
14
|
-
isScrolledBottom: false, // Determines if the container is at its bottommost position
|
|
15
|
-
maxScrollLeft: 0, // Maximum possible scroll on the X-axis
|
|
16
|
-
maxScrollTop: 0, // Maximum possible scroll on the Y-axis
|
|
17
|
-
scrollPercentageX: 0, // Percentage of horizontal scroll covered
|
|
18
|
-
scrollPercentageY: 0, // Percentage of vertical scroll covered
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function useScroll<T extends HTMLElement>(props?: Partial<ScrollOptions>) {
|
|
22
|
-
const ref = useRef<T>(null)
|
|
23
|
-
const scrollOpt = { ...defaultScrollOptions, ...props }
|
|
24
|
-
const scrollerRef = useRef<ReturnType<typeof createScroller> | null>(null)
|
|
25
|
-
const [state, setState] = useState(() => initialState)
|
|
26
|
-
|
|
27
|
-
useIsoMorphicEffect(() => {
|
|
28
|
-
const scrollContainer = ref.current
|
|
29
|
-
if (!scrollContainer) return
|
|
30
|
-
|
|
31
|
-
// Create the scroller instance only once inside the effect
|
|
32
|
-
if (!scrollerRef.current) {
|
|
33
|
-
const container = getElement(ref)
|
|
34
|
-
scrollerRef.current = createScroller(container, scrollOpt)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const { scrollWidth, clientWidth, clientHeight, scrollHeight } = scrollContainer
|
|
38
|
-
const isScrollable = clientWidth !== scrollWidth || clientHeight !== scrollHeight
|
|
39
|
-
setState((prev) => ({ ...prev, isScrollable }))
|
|
40
|
-
|
|
41
|
-
const handleScroll = () => {
|
|
42
|
-
if (scrollContainer) {
|
|
43
|
-
const { scrollLeft, scrollTop } = scrollContainer
|
|
44
|
-
const isAtLeftBoundary = scrollLeft === 0
|
|
45
|
-
const isAtRightBoundary = scrollLeft >= scrollWidth - clientWidth
|
|
46
|
-
const isAtTopBoundary = scrollTop === 0
|
|
47
|
-
const isAtBottomBoundary = scrollTop >= scrollHeight - clientHeight
|
|
48
|
-
const scrollState = {
|
|
49
|
-
...state,
|
|
50
|
-
isScrolledLeft: isAtLeftBoundary,
|
|
51
|
-
isScrolledRight: isAtRightBoundary,
|
|
52
|
-
isScrolledTop: isAtTopBoundary,
|
|
53
|
-
isScrolledBottom: isAtBottomBoundary,
|
|
54
|
-
left: scrollLeft,
|
|
55
|
-
top: scrollTop,
|
|
56
|
-
maxScrollLeft: scrollWidth - clientWidth,
|
|
57
|
-
maxScrollTop: scrollHeight - clientHeight,
|
|
58
|
-
scrollPercentageX: isAtLeftBoundary
|
|
59
|
-
? 0
|
|
60
|
-
: (scrollLeft / (scrollWidth - clientWidth)) * 100,
|
|
61
|
-
scrollPercentageY: isAtTopBoundary
|
|
62
|
-
? 0
|
|
63
|
-
: (scrollTop / (scrollHeight - clientHeight)) * 100,
|
|
64
|
-
}
|
|
65
|
-
setState(scrollState)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
handleScroll()
|
|
70
|
-
scrollContainer?.addEventListener('scroll', handleScroll)
|
|
71
|
-
return () => scrollContainer?.removeEventListener('scroll', handleScroll)
|
|
72
|
-
}, [state])
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
ref,
|
|
76
|
-
state,
|
|
77
|
-
scrollLeft: scrollerRef.current?.scrollToLeft,
|
|
78
|
-
scrollRight: scrollerRef.current?.scrollToRight,
|
|
79
|
-
scrollBottom: scrollerRef.current?.scrollToBottom,
|
|
80
|
-
scrollTop: scrollerRef.current?.scrollToTop,
|
|
81
|
-
scrollCenter: scrollerRef.current?.scrollToCenter,
|
|
82
|
-
scrollTo: scrollerRef.current?.scrollTo,
|
|
83
|
-
}
|
|
84
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { useCallback, useState } from 'react'
|
|
2
|
-
import { createScroller } from '../scroller'
|
|
3
|
-
import { ScrollOptions } from '../types'
|
|
4
|
-
import { useIsoMorphicEffect } from './use-iso-morphic-effect'
|
|
5
|
-
import { defaultScrollOptions, resolveScrollValues } from '../utils'
|
|
6
|
-
|
|
7
|
-
export function useWindowScroll(props?: Partial<Omit<ScrollOptions, 'direction'>>) {
|
|
8
|
-
const [state, setState] = useState(() => ({
|
|
9
|
-
left: 0,
|
|
10
|
-
top: 0,
|
|
11
|
-
isScrolledTop: false,
|
|
12
|
-
isScrolledBottom: false,
|
|
13
|
-
}))
|
|
14
|
-
|
|
15
|
-
const container = window
|
|
16
|
-
const scrollOptions = { ...defaultScrollOptions, ...props }
|
|
17
|
-
|
|
18
|
-
const scrollContainer = createScroller(container, scrollOptions)
|
|
19
|
-
|
|
20
|
-
const handleScroll = useCallback(() => {
|
|
21
|
-
const { scrollLeft, scrollTop, clientHeight, scrollHeight } = resolveScrollValues(container)
|
|
22
|
-
|
|
23
|
-
const isAtTopBoundary = scrollTop === 0
|
|
24
|
-
const isAtBottomBoundary = scrollTop >= scrollHeight - clientHeight
|
|
25
|
-
|
|
26
|
-
const scrollState = {
|
|
27
|
-
left: scrollLeft,
|
|
28
|
-
top: scrollTop,
|
|
29
|
-
isScrolledTop: isAtTopBoundary,
|
|
30
|
-
isScrolledBottom: isAtBottomBoundary,
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
setState(scrollState)
|
|
34
|
-
}, [container])
|
|
35
|
-
|
|
36
|
-
useIsoMorphicEffect(() => {
|
|
37
|
-
handleScroll()
|
|
38
|
-
container.addEventListener('scroll', handleScroll)
|
|
39
|
-
return () => {
|
|
40
|
-
container.removeEventListener('scroll', handleScroll)
|
|
41
|
-
}
|
|
42
|
-
}, [handleScroll])
|
|
43
|
-
|
|
44
|
-
const scrollTo = useCallback(
|
|
45
|
-
(position: number) => {
|
|
46
|
-
scrollContainer.scrollTo(position)
|
|
47
|
-
},
|
|
48
|
-
[scrollContainer]
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
const scrollToTarget = useCallback(
|
|
52
|
-
(currentTarget: HTMLElement) => {
|
|
53
|
-
scrollContainer.scrollToTarget(currentTarget)
|
|
54
|
-
},
|
|
55
|
-
[scrollContainer]
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
const scrollTop = useCallback(
|
|
59
|
-
(offset?: number | undefined) => {
|
|
60
|
-
scrollContainer.scrollToTop(offset)
|
|
61
|
-
},
|
|
62
|
-
[scrollContainer]
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
const scrollBottom = useCallback(
|
|
66
|
-
(offset?: number | undefined) => {
|
|
67
|
-
scrollContainer.scrollToBottom(offset)
|
|
68
|
-
},
|
|
69
|
-
[scrollContainer]
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
return { state, scrollTop, scrollBottom, scrollToTarget, scrollTo }
|
|
73
|
-
}
|
package/src/index.ts
DELETED
package/src/scroller.ts
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { ScrollOptions } from './types'
|
|
2
|
-
import { defaultScrollOptions, getEasing, resolveScrollValues, validateElement } from './utils'
|
|
3
|
-
|
|
4
|
-
export function createScroller(
|
|
5
|
-
container: HTMLElement | Window,
|
|
6
|
-
options: ScrollOptions = defaultScrollOptions
|
|
7
|
-
) {
|
|
8
|
-
validateElement(container)
|
|
9
|
-
const { duration, easingOption, direction } = options
|
|
10
|
-
const { scrollLeft, scrollTop, clientWidth, clientHeight, scrollWidth, scrollHeight } =
|
|
11
|
-
resolveScrollValues(container)
|
|
12
|
-
|
|
13
|
-
function scroll(from: number, to: number, layout: ScrollOptions['direction'] = direction) {
|
|
14
|
-
let startTime: number | null = null
|
|
15
|
-
let requestId = 0
|
|
16
|
-
const distance = Math.abs(to - from)
|
|
17
|
-
|
|
18
|
-
function calculateCurrentPosition(timeFraction: number): number {
|
|
19
|
-
const easingFunc = getEasing(easingOption)
|
|
20
|
-
return easingFunc(timeFraction) * distance + from
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function updateScrollPosition(currentPosition: number) {
|
|
24
|
-
container.scrollTo({
|
|
25
|
-
left: layout === 'horizontal' ? currentPosition : undefined,
|
|
26
|
-
top: layout === 'vertical' ? currentPosition : undefined,
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function animateScroll(currentTime: number) {
|
|
31
|
-
if (!startTime) startTime = currentTime
|
|
32
|
-
const elapsedTime = currentTime - startTime
|
|
33
|
-
const timeFraction = elapsedTime / duration
|
|
34
|
-
const currentPosition = calculateCurrentPosition(timeFraction)
|
|
35
|
-
|
|
36
|
-
updateScrollPosition(currentPosition)
|
|
37
|
-
|
|
38
|
-
if (timeFraction < 1) {
|
|
39
|
-
requestId = requestAnimationFrame(animateScroll)
|
|
40
|
-
} else {
|
|
41
|
-
cancelAnimationFrame(requestId)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
requestId = requestAnimationFrame(animateScroll)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function scrollTo(to: number) {
|
|
49
|
-
const from = direction === 'vertical' ? scrollTop : scrollLeft
|
|
50
|
-
scroll(from, to)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function scrollToCenter() {
|
|
54
|
-
let from = 0
|
|
55
|
-
let to = 0
|
|
56
|
-
|
|
57
|
-
if (direction === 'vertical') {
|
|
58
|
-
const scrollYCenterPosition = (scrollHeight - clientHeight) / 2
|
|
59
|
-
from = scrollTop
|
|
60
|
-
to = scrollYCenterPosition
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (direction === 'horizontal') {
|
|
64
|
-
const scrollXCenterPosition = (scrollWidth - clientWidth) / 2
|
|
65
|
-
from = scrollLeft
|
|
66
|
-
to = scrollXCenterPosition
|
|
67
|
-
}
|
|
68
|
-
scroll(from, to, direction)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function scrollToTarget(target: HTMLElement, offset = 0) {
|
|
72
|
-
validateElement(target)
|
|
73
|
-
const { offsetTop, offsetLeft } = target
|
|
74
|
-
let from = 0
|
|
75
|
-
let to = 0
|
|
76
|
-
|
|
77
|
-
if (direction === 'vertical') {
|
|
78
|
-
from = scrollTop
|
|
79
|
-
to = offsetTop - offset
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (direction === 'horizontal') {
|
|
83
|
-
from = scrollLeft
|
|
84
|
-
to = offsetLeft - offset
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
scroll(from, to, direction)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
scrollTo,
|
|
92
|
-
scrollToCenter,
|
|
93
|
-
scrollToTarget,
|
|
94
|
-
scrollToLeft: (offset?: number) => {
|
|
95
|
-
scroll(scrollLeft, offset || scrollLeft - clientWidth, 'horizontal')
|
|
96
|
-
},
|
|
97
|
-
scrollToRight: (offset?: number) => {
|
|
98
|
-
scroll(scrollLeft, offset || scrollLeft + clientWidth, 'horizontal')
|
|
99
|
-
},
|
|
100
|
-
scrollToTop: (offset?: number) => {
|
|
101
|
-
scroll(scrollTop, offset || scrollTop - clientHeight, 'vertical')
|
|
102
|
-
},
|
|
103
|
-
scrollToBottom: (offset?: number) => {
|
|
104
|
-
scroll(scrollTop, offset || scrollTop + clientHeight, 'vertical')
|
|
105
|
-
},
|
|
106
|
-
}
|
|
107
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export type ScrollOptions = {
|
|
2
|
-
direction: 'horizontal' | 'vertical'
|
|
3
|
-
duration: number
|
|
4
|
-
easingOption: EasingOptions
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export type ScrollerProps = {
|
|
8
|
-
container: HTMLElement | Window
|
|
9
|
-
options: ScrollOptions
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type EasingOptions = 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out'
|
|
13
|
-
|
|
14
|
-
export type EasingFunction = (t: number) => number
|
|
15
|
-
|
|
16
|
-
export interface EasingFunctions {
|
|
17
|
-
linear: EasingFunction
|
|
18
|
-
'ease-in': EasingFunction
|
|
19
|
-
'ease-out': EasingFunction
|
|
20
|
-
'ease-in-out': EasingFunction
|
|
21
|
-
}
|
package/src/utils.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { RefObject } from 'react'
|
|
2
|
-
import { EasingFunction, EasingFunctions, EasingOptions, ScrollOptions } from './types'
|
|
3
|
-
|
|
4
|
-
// Check if the code is running on the server-side.
|
|
5
|
-
export const isServer = typeof window === 'undefined' || typeof document === 'undefined'
|
|
6
|
-
|
|
7
|
-
// Default scroll options used if no options are provided.
|
|
8
|
-
export const defaultScrollOptions: ScrollOptions = {
|
|
9
|
-
direction: 'vertical',
|
|
10
|
-
duration: 300,
|
|
11
|
-
easingOption: 'ease-in-out',
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Resolves scroll values based on the type of element.
|
|
16
|
-
* @param {HTMLElement | Window} element - The element to retrieve scroll values from.
|
|
17
|
-
* @returns {object} - An object containing scroll-related properties.
|
|
18
|
-
*/
|
|
19
|
-
export const resolveScrollValues = (element: HTMLElement | Window) => {
|
|
20
|
-
const isWindow = element instanceof Window
|
|
21
|
-
const docElement = document.documentElement
|
|
22
|
-
const docBody = document.body
|
|
23
|
-
|
|
24
|
-
const scrollLeftMax = isWindow
|
|
25
|
-
? Math.max(docBody.scrollLeft, docElement.scrollLeft)
|
|
26
|
-
: element.scrollWidth - element.clientWidth
|
|
27
|
-
const scrollTopMax = isWindow
|
|
28
|
-
? Math.max(docBody.scrollTop, docElement.scrollTop)
|
|
29
|
-
: element.scrollHeight - element.clientHeight
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
scrollLeft: isWindow ? docBody.scrollLeft || docElement.scrollLeft : element.scrollLeft,
|
|
33
|
-
scrollTop: isWindow ? docBody.scrollTop || docElement.scrollTop : element.scrollTop,
|
|
34
|
-
scrollWidth: isWindow ? docBody.scrollWidth || docElement.scrollWidth : element.scrollWidth,
|
|
35
|
-
clientWidth: isWindow ? docBody.clientWidth || docElement.clientWidth : element.clientWidth,
|
|
36
|
-
clientHeight: isWindow ? docBody.clientHeight || docElement.clientHeight : element.clientHeight,
|
|
37
|
-
scrollHeight: isWindow ? docBody.scrollHeight || docElement.scrollHeight : element.scrollHeight,
|
|
38
|
-
scrollLeftMax,
|
|
39
|
-
scrollTopMax,
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Validate if the element passed to the function is valid.
|
|
45
|
-
* @param {HTMLElement | Window} element - The element to validate.
|
|
46
|
-
*/
|
|
47
|
-
export function validateElement(element?: HTMLElement | Window) {
|
|
48
|
-
if (element === undefined) {
|
|
49
|
-
throw new Error(`The element passed to scroller() was undefined`)
|
|
50
|
-
}
|
|
51
|
-
if (!(element instanceof HTMLElement || Window)) {
|
|
52
|
-
throw new TypeError(
|
|
53
|
-
`
|
|
54
|
-
The element passed to scroller() must be a valid element.
|
|
55
|
-
You passed ${element}.
|
|
56
|
-
`
|
|
57
|
-
)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Get the DOM element from a React ref object.
|
|
63
|
-
* @param {RefObject<HTMLElement>} ref - The React ref object.
|
|
64
|
-
* @returns {HTMLElement} - The DOM element.
|
|
65
|
-
*/
|
|
66
|
-
export const getElement = (ref: RefObject<HTMLElement>) => {
|
|
67
|
-
if (!ref.current) {
|
|
68
|
-
throw new Error(`Could not resolve ref object: ${ref.current}`)
|
|
69
|
-
}
|
|
70
|
-
return ref.current
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Map of easing functions for scroll animations.
|
|
74
|
-
export const easingMap: EasingFunctions = {
|
|
75
|
-
linear(t: number) {
|
|
76
|
-
return t
|
|
77
|
-
},
|
|
78
|
-
'ease-in'(t: number) {
|
|
79
|
-
return t * t
|
|
80
|
-
},
|
|
81
|
-
'ease-out'(t: number) {
|
|
82
|
-
return t * (2 - t)
|
|
83
|
-
},
|
|
84
|
-
'ease-in-out'(t: number) {
|
|
85
|
-
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
|
|
86
|
-
},
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Get an easing function based on the provided easing option or the default option.
|
|
91
|
-
* @param {EasingOptions} easing - The easing option.
|
|
92
|
-
* @returns {EasingFunction} - The easing function.
|
|
93
|
-
*/
|
|
94
|
-
export const getEasing = (easing?: EasingOptions): EasingFunction => {
|
|
95
|
-
const easeFunc = easingMap[easing || defaultScrollOptions.easingOption]
|
|
96
|
-
if (!easeFunc) {
|
|
97
|
-
const options = Object.keys(easingMap).join(',')
|
|
98
|
-
throw new Error(
|
|
99
|
-
`Scroll error: scroller does not support an easing option of "${easing}". Supported options are ${options}`
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
return easeFunc
|
|
103
|
-
}
|