Skip to content

Commit 17f822c

Browse files
committed
fix: compute scroll position in effect
1 parent 2b2a376 commit 17f822c

File tree

2 files changed

+84
-80
lines changed

2 files changed

+84
-80
lines changed

lib/LayeredImage.tsx

Lines changed: 84 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import * as React from "react"
22
import { createRef, useEffect, useMemo, useRef, useState } from "react"
33

4-
import { applyStyles, clamp, isBrowser, isFunction } from "./utils"
4+
import { applyStyles, clamp, isFunction } from "./utils"
55

66
interface Size {
77
width: number
88
height: number
99
}
1010

11+
interface Position {
12+
x: number
13+
y: number
14+
}
15+
1116
enum Interaction {
1217
None = "NONE",
1318
Resize = "RESIZE",
@@ -50,6 +55,7 @@ export const LayeredImage: React.FC<ILayeredImageProps> = ({
5055
style,
5156
}) => {
5257
const [size, setSize] = useState<Size>({ width: 0, height: 0 })
58+
const [scrollPosition, setScrollPosition] = useState<Position>({ x: 0, y: 0 })
5359
const [interaction, setInteraction] = useState<Interaction>(Interaction.None)
5460
const [loaded, setLoaded] = useState<number>(0)
5561
const [, setError] = useState<number>(0)
@@ -125,101 +131,88 @@ export const LayeredImage: React.FC<ILayeredImageProps> = ({
125131
pageY?: number,
126132
preventDefault = false,
127133
) => {
128-
if (isBrowser) {
129-
const { width, height } = _interaction === Interaction.Resize ? getDimensions() : size
134+
const { width, height } = _interaction === Interaction.Resize ? getDimensions() : size
135+
const { x: scrollLeft, y: scrollTop } = scrollPosition
130136

131-
const bodyScrollTop =
132-
document.body.scrollTop ||
133-
document.documentElement.scrollTop ||
134-
document.scrollingElement.scrollTop ||
135-
window.scrollY ||
136-
window.pageYOffset
137-
const bodyScrollLeft =
138-
document.body.scrollLeft ||
139-
document.documentElement.scrollLeft ||
140-
document.scrollingElement.scrollLeft ||
141-
window.scrollX ||
142-
window.pageXOffset
143-
const containerRect = elementsRef.current.container.current.getBoundingClientRect()
137+
const containerRect = elementsRef.current.container.current.getBoundingClientRect()
144138

145-
const offsetX = (pageX - containerRect.left - bodyScrollLeft) / width
146-
const offsetY = (pageY - containerRect.top - bodyScrollTop) / height
147-
const containerCenterX = pageX - containerRect.left - bodyScrollLeft - width / 2
148-
const containerCenterY = pageY - containerRect.top - bodyScrollTop - height / 2
139+
const offsetX = (pageX - containerRect.left - scrollLeft) / width
140+
const offsetY = (pageY - containerRect.top - scrollTop) / height
141+
const containerCenterX = pageX - containerRect.left - scrollLeft - width / 2
142+
const containerCenterY = pageY - containerRect.top - scrollTop - height / 2
149143

150-
const containerRotationX = ((offsetY - containerCenterY) / (height / 2)) * 8
151-
const containerRotationY = ((containerCenterX - offsetX) / (width / 2)) * 8
152-
const layerTranslationX = (offsetX - containerCenterX) * 0.01
153-
const layerTranslationY = (offsetY - containerCenterY) * 0.01
154-
const lightAngle = (Math.atan2(containerCenterY, containerCenterX) * 180) / Math.PI - 90
144+
const containerRotationX = ((offsetY - containerCenterY) / (height / 2)) * 8
145+
const containerRotationY = ((containerCenterX - offsetX) / (width / 2)) * 8
146+
const layerTranslationX = (offsetX - containerCenterX) * 0.01
147+
const layerTranslationY = (offsetY - containerCenterY) * 0.01
148+
const lightAngle = (Math.atan2(containerCenterY, containerCenterX) * 180) / Math.PI - 90
155149

156-
const computedStyles: ILayeredImageStyles = {
157-
[Interaction.None]: defaultStyles,
158-
[Interaction.Resize]: {
159-
root: {
160-
height: `${height}px`,
161-
transform: `perspective(${width * 3}px)`,
162-
},
150+
const computedStyles: ILayeredImageStyles = {
151+
[Interaction.None]: defaultStyles,
152+
[Interaction.Resize]: {
153+
root: {
154+
height: `${height}px`,
155+
transform: `perspective(${width * 3}px)`,
163156
},
164-
[Interaction.Hover]: {
165-
container: {
166-
transform: `rotateX(${-clamp(containerRotationX, -8, 8)}deg)
157+
},
158+
[Interaction.Hover]: {
159+
container: {
160+
transform: `rotateX(${-clamp(containerRotationX, -8, 8)}deg)
167161
rotateY(${-clamp(containerRotationY, -8, 8)}deg)
168162
translateX(${-layerTranslationX * 5}px)
169163
translateY(${-layerTranslationY * 5}px)
170164
scale(1.1)`,
171-
},
172-
layer: (index: number) => ({
173-
transform: `translateX(${clamp(layerTranslationX, -2, 2) * 1.4 * index}px)
165+
},
166+
layer: (index: number) => ({
167+
transform: `translateX(${clamp(layerTranslationX, -2, 2) * 1.4 * index}px)
174168
translateY(${clamp(layerTranslationY, -2, 2) * 1.4 * index}px)
175169
scale(1.04)`,
176-
}),
177-
light: {
178-
backgroundImage: `linear-gradient(${lightAngle}deg, ${lightColor} 0%, transparent 80%)`,
179-
},
180-
shadow: {
181-
boxShadow: `0 40px 100px ${shadowColor}, 0 10px 20px ${shadowColor}`,
182-
},
170+
}),
171+
light: {
172+
backgroundImage: `linear-gradient(${lightAngle}deg, ${lightColor} 0%, transparent 80%)`,
183173
},
184-
[Interaction.Active]: {
185-
container: {
186-
transitionDuration: "0.075s",
187-
transform: `rotateX(${containerRotationX / 1.4}deg)
174+
shadow: {
175+
boxShadow: `0 40px 100px ${shadowColor}, 0 10px 20px ${shadowColor}`,
176+
},
177+
},
178+
[Interaction.Active]: {
179+
container: {
180+
transitionDuration: "0.075s",
181+
transform: `rotateX(${containerRotationX / 1.4}deg)
188182
rotateY(${containerRotationY / 1.4}deg)
189183
scale(1)`,
190-
},
191-
layer: (index: number) => ({
192-
transform: `translateX(${-layerTranslationX * index}px)
184+
},
185+
layer: (index: number) => ({
186+
transform: `translateX(${-layerTranslationX * index}px)
193187
translateY(${-layerTranslationY * index}px)
194188
scale(1.02)`,
195-
}),
196-
light: {
197-
backgroundImage: `linear-gradient(${lightAngle}deg, ${lightColor} 0%, transparent 80%)`,
198-
},
199-
shadow: {
200-
...defaultStyles.shadow,
201-
transitionDuration: "0.075s",
202-
},
189+
}),
190+
light: {
191+
backgroundImage: `linear-gradient(${lightAngle}deg, ${lightColor} 0%, transparent 80%)`,
203192
},
204-
}[_interaction]
193+
shadow: {
194+
...defaultStyles.shadow,
195+
transitionDuration: "0.075s",
196+
},
197+
},
198+
}[_interaction]
205199

206-
if (preventDefault) {
207-
event.preventDefault()
208-
}
200+
if (preventDefault) {
201+
event.preventDefault()
202+
}
209203

210-
for (const [element, styles] of Object.entries(computedStyles)) {
211-
if (element === "layer") {
212-
layers.forEach((_, index) =>
213-
applyStyles(elementsRef.current.layers[index].current, isFunction(styles) ? styles(index) : styles),
214-
)
215-
} else {
216-
applyStyles(elementsRef.current[element].current, styles)
217-
}
204+
for (const [element, styles] of Object.entries(computedStyles)) {
205+
if (element === "layer") {
206+
layers.forEach((_, index) =>
207+
applyStyles(elementsRef.current.layers[index].current, isFunction(styles) ? styles(index) : styles),
208+
)
209+
} else {
210+
applyStyles(elementsRef.current[element].current, styles)
218211
}
219-
220-
setSize({ width, height })
221-
setInteraction(_interaction)
222212
}
213+
214+
setSize({ width, height })
215+
setInteraction(_interaction)
223216
}
224217

225218
// prettier-ignore
@@ -238,6 +231,20 @@ export const LayeredImage: React.FC<ILayeredImageProps> = ({
238231

239232
useEffect(() => {
240233
const handleWindowResize = () => computeStyles(Interaction.Resize)
234+
const handleWindowScroll = () => {
235+
setScrollPosition({
236+
x:
237+
document.documentElement.scrollLeft ||
238+
document.scrollingElement.scrollLeft ||
239+
window.scrollX ||
240+
window.pageXOffset,
241+
y:
242+
document.documentElement.scrollTop ||
243+
document.scrollingElement.scrollTop ||
244+
window.scrollY ||
245+
window.pageYOffset,
246+
})
247+
}
241248

242249
layers.forEach((layer) => {
243250
const image = new Image()
@@ -248,9 +255,11 @@ export const LayeredImage: React.FC<ILayeredImageProps> = ({
248255
})
249256

250257
window.addEventListener("resize", handleWindowResize)
258+
window.addEventListener("scroll", handleWindowScroll)
251259

252260
return () => {
253261
window.removeEventListener("resize", handleWindowResize)
262+
window.removeEventListener("scroll", handleWindowScroll)
254263
}
255264
}, [])
256265

lib/utils.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@ export const clamp = (value: number, min: number, max: number) => {
2222
return value != null ? Math.min(Math.max(value, min), maximum) : value
2323
}
2424

25-
/**
26-
* Check whether the code is running in the browser.
27-
*/
28-
export const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined"
29-
3025
/**
3126
* Return `true` if the value is a `function`.
3227
*/

0 commit comments

Comments
 (0)