Skip to content

Commit

Permalink
Honor user's reduced motion preference for backdrop animations
Browse files Browse the repository at this point in the history
Disable scroll parallax and autozoom if the userhas enabled a setting
on their device to minimize the amount of non-essential motion.
  • Loading branch information
tf committed Jul 12, 2024
1 parent 54d0282 commit 85ccce8
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,33 @@ describe('backdrop animation effects', () => {
expect(animateMock).not.toHaveBeenCalled();
});

it('neither calls animate nor sets up view timeline when reduced motion is preferred', () => {
window.matchMedia.mockPrefersReducedMotion();

renderEntry({
seed: {
imageFiles: [{permaId: 100}],
sections: [
{
permaId: 1,
configuration: {
backdrop: {image: 100},
backdropEffects: [
{
name: 'scrollParallax',
value: 40
}
]
}
}
]
}
});

expect(viewTimelines.length).toEqual(0);
expect(animateMock).not.toHaveBeenCalled();
});

it('supports auto zoom', () => {
const {getSectionByPermaId} = renderEntry({
seed: {
Expand Down Expand Up @@ -245,4 +272,32 @@ describe('backdrop animation effects', () => {

expect(animateMock).not.toHaveBeenCalled();
});

it('does not autozoom if reduced motion is preferred', () => {
window.matchMedia.mockPrefersReducedMotion();

const {getSectionByPermaId} = renderEntry({
seed: {
imageFiles: [{permaId: 100}],
sections: [
{
permaId: 1,
configuration: {
backdrop: {image: 100},
backdropEffects: [
{
name: 'autoZoom',
value: 50
}
]
}
}
]
}
});

getSectionByPermaId(1).simulateScrollingIntoView();

expect(animateMock).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,4 @@ describe('Backdrop Effects getFilter', () => {

expect(result).toEqual('grayscale(20%)');
});

it('sets up scroll animation ', () => {

});
});
14 changes: 13 additions & 1 deletion entry_types/scrolled/package/spec/support/matchMediaStub.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
let mockOrientation;
let mockPrefersReducedMotion;

Object.defineProperty(window, 'matchMedia', {
writable: true,
Expand All @@ -17,6 +18,13 @@ Object.defineProperty(window, 'matchMedia', {
matches: mockOrientation !== 'portrait'
};
}
else if (query === '(prefers-reduced-motion)') {
return {
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
matches: mockPrefersReducedMotion
};
}
else {
return {
addEventListener: jest.fn(),
Expand All @@ -27,6 +35,10 @@ Object.defineProperty(window, 'matchMedia', {
})
});

beforeEach(() => mockOrientation = 'landscape');
beforeEach(() => {
mockOrientation = 'landscape';
mockPrefersReducedMotion = false;
});

window.matchMedia.mockPortrait = () => mockOrientation = 'portrait';
window.matchMedia.mockPrefersReducedMotion = () => mockPrefersReducedMotion = true;
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, {useContext, useEffect, useState, useRef} from 'react';

import {prefersReducedMotion} from './prefersReducedMotion';

const SectionViewTimelineContext = React.createContext();

export function SectionViewTimelineProvider({backdrop, children}) {
Expand All @@ -9,7 +11,7 @@ export function SectionViewTimelineProvider({backdrop, children}) {
const isNeeded = backdrop?.effects?.some(effect => effect.name === 'scrollParallax');

useEffect(() => {
if (!isNeeded || !window.ViewTimeline) {
if (!isNeeded || !window.ViewTimeline || prefersReducedMotion()) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function prefersReducedMotion() {
return window.matchMedia('(prefers-reduced-motion)').matches;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import styles from '../../Backdrop.module.css';
import {useSectionViewTimeline} from '../../SectionViewTimelineProvider';
import {useSectionLifecycle} from '../../useSectionLifecycle';
import {useIsStaticPreview} from '../../useScrollPositionLifecycle';
import {prefersReducedMotion} from '../../prefersReducedMotion';

export function Effects({file, children}) {
const ref = useRef();
Expand Down Expand Up @@ -45,7 +46,7 @@ export function Effects({file, children}) {
const y = file?.motifArea ? 50 - (file.motifArea.top + file.motifArea.height / 2) : 0;

useIsomorphicLayoutEffect(() => {
if (autoZoomValue && isVisible) {
if (autoZoomValue && isVisible && !prefersReducedMotion()) {
const animation = ref.current.animate(
{
transform: [
Expand Down

0 comments on commit 85ccce8

Please sign in to comment.