/
use-scroll.ts
61 lines (53 loc) · 1.87 KB
/
use-scroll.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import { scroll, ScrollOptions } from "@motionone/dom"
import { RefObject } from "react"
import { motionValue } from "."
import { useConstant } from "../utils/use-constant"
import { useEffect } from "react"
import { useIsomorphicLayoutEffect } from "../three-entry"
import { warning } from "hey-listen"
interface UseScrollOptions extends Omit<ScrollOptions, "container" | "target"> {
container?: RefObject<HTMLElement>
target?: RefObject<HTMLElement>
layoutEffect?: boolean
}
function refWarning(name: string, ref?: RefObject<HTMLElement>) {
warning(
Boolean(!ref || ref.current),
`You have defined a ${name} options but the provided ref is not yet hydrated, probably because it's defined higher up the tree. Try calling useScroll() in the same component as the ref, or setting its \`layoutEffect: false\` option.`
)
}
const createScrollMotionValues = () => ({
scrollX: motionValue(0),
scrollY: motionValue(0),
scrollXProgress: motionValue(0),
scrollYProgress: motionValue(0),
})
export function useScroll({
container,
target,
layoutEffect = true,
...options
}: UseScrollOptions = {}) {
const values = useConstant(createScrollMotionValues)
const useLifecycleEffect = layoutEffect
? useIsomorphicLayoutEffect
: useEffect
useLifecycleEffect(() => {
refWarning("target", target)
refWarning("container", container)
return scroll(
({ x, y }) => {
values.scrollX.set(x.current)
values.scrollXProgress.set(x.progress)
values.scrollY.set(y.current)
values.scrollYProgress.set(y.progress)
},
{
...options,
container: container?.current || undefined,
target: target?.current || undefined,
}
)
}, [])
return values
}