Skip to content

Commit fe6dfca

Browse files
committed
turn off of reduced motion is set on user preferences #136
1 parent 6717a6d commit fe6dfca

File tree

2 files changed

+49
-9
lines changed

2 files changed

+49
-9
lines changed

src/react/hooks/useReduceMotion.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useEffect, useState } from 'react';
2+
3+
const useReducedMotion = (): boolean => {
4+
const [prefersReducedMotion, setPrefersReducedMotion] = useState<boolean>(false);
5+
6+
useEffect(() => {
7+
if (typeof window === 'undefined') {
8+
return;
9+
}
10+
11+
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
12+
setPrefersReducedMotion(mediaQuery.matches);
13+
14+
const handleMotionPreferenceChange = (event: MediaQueryListEvent) => {
15+
setPrefersReducedMotion(event.matches);
16+
};
17+
18+
mediaQuery.addEventListener('change', handleMotionPreferenceChange);
19+
20+
return () => {
21+
mediaQuery.removeEventListener('change', handleMotionPreferenceChange);
22+
};
23+
}, []);
24+
25+
return prefersReducedMotion;
26+
};
27+
28+
export default useReducedMotion;

src/react/index.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import AnimationManager from "./AnimationManager";
33
import useGetImageHeight from "./hooks/useGetImageHeight";
44
import useGetTransitionValue from "./hooks/useGetTransitionValue";
55
import useIntersectionObserver from "./hooks/useIntersectionObserver";
6+
import useReducedMotion from "./hooks/useReduceMotion";
67
import { SimpleParallaxProps } from "./types";
78

89
const SimpleParallax: React.FunctionComponent<SimpleParallaxProps> = ({
@@ -26,7 +27,8 @@ const SimpleParallax: React.FunctionComponent<SimpleParallaxProps> = ({
2627
const [transformCSS, setTransformCSS] = useState("");
2728
const [transitionCSS, setTransitionCSS] = useState("");
2829
const [shouldApplyTransition, setShouldApplyTransition] = useState(false);
29-
30+
31+
const prefersReducedMotion = useReducedMotion();
3032
const [imageRef, imageHeight, isLoaded] = useGetImageHeight(src);
3133
const [elementRef, isVisible] = useIntersectionObserver<HTMLDivElement>({
3234
root: null,
@@ -43,7 +45,7 @@ const SimpleParallax: React.FunctionComponent<SimpleParallaxProps> = ({
4345
});
4446

4547
const updateParallax = useCallback(() => {
46-
if (!isVisible && isInit) return;
48+
if ((!isVisible && isInit) || prefersReducedMotion) return;
4749

4850
if (window.scrollY !== viewportTop || !isInit) {
4951
const boundingClientRect = imageRef.current?.getBoundingClientRect();
@@ -52,43 +54,53 @@ const SimpleParallax: React.FunctionComponent<SimpleParallaxProps> = ({
5254
}
5355
if (!isInit) {
5456
setIsInit(true);
57+
// We'll enable transitions after the first calculation
5558
setTimeout(() => {
5659
setShouldApplyTransition(true);
5760
}, 50);
5861
}
5962
setViewportTop(window.scrollY);
6063
}
61-
}, [viewportTop, isVisible, imageRef, isInit]);
64+
}, [viewportTop, isVisible, imageRef, isInit, prefersReducedMotion]);
6265

6366
useEffect(() => {
67+
if (prefersReducedMotion) {
68+
setTransformCSS("");
69+
return;
70+
}
71+
6472
let transform = `translate3d(${transitionValue})`;
6573
if (!overflow) {
6674
transform += ` scale(${scale})`;
6775
}
6876
setTransformCSS(transform);
69-
}, [transitionValue, scale, overflow]);
77+
}, [transitionValue, scale, overflow, prefersReducedMotion]);
7078

7179
useEffect(() => {
72-
if (!transition || !delay || !shouldApplyTransition) {
80+
if (!transition || !delay || !shouldApplyTransition || prefersReducedMotion) {
7381
setTransitionCSS("");
7482
return;
7583
}
7684
setTransitionCSS(`transform ${delay}s ${transition}`);
77-
}, [transition, delay, shouldApplyTransition]);
85+
}, [transition, delay, shouldApplyTransition, prefersReducedMotion]);
7886

7987
useEffect(() => {
80-
AnimationManager.register(updateParallax);
88+
// Only register for animation if reduced motion is not preferred
89+
if (!prefersReducedMotion) {
90+
AnimationManager.register(updateParallax);
91+
}
92+
8193
return () => {
8294
AnimationManager.unregister(updateParallax);
8395
};
84-
}, [updateParallax]);
96+
}, [updateParallax, prefersReducedMotion]);
8597

8698
const clonedChild = React.isValidElement(children)
8799
? React.cloneElement(children as React.ReactElement, {
88100
style: {
89101
...((children as React.ReactElement).props.style ?? {}),
90102
transform: transformCSS,
91-
willChange: "transform",
103+
willChange: prefersReducedMotion ? "auto" : "transform",
92104
transition: transitionCSS,
93105
},
94106
ref: imageRef,

0 commit comments

Comments
 (0)