Closed evanpurkhiser closed 5 months ago
I have a really crude temporary workaround for this involving using a combination of <MotionConfig reducedMotion="always">
to disable all transitions involving movement, and a * { opacity: 1 !important; }
global style to disable opacity transitions, and so far that seems to have disabled everything I needed.
Though the obvious severe caveat here is that this means opacity styles won't show up in tests, without tacking on another layer of !important
(which would break opacity transitions in non-test environments).
I'd be happy to submit a PR to implement this properly if the maintainers (cc @mattgperry?) are open to accepting it! I'm thinking the API and implementation could be analogous to reducedMotion
in MotionConfig
, except just a disable
boolean prop that disables all transitions, not just ones that cause movement.
We would quite like something like this in Framer too so I might be able to take a look in the next couple
Very interested in this.
I need to generate screenshots of my app using Puppeteer, and being able to skip/disable all animations globally would be awesome.
Does anyone have workarounds to make this work?
Our solution:
import {
type ForwardRefComponent,
type HTMLMotionProps,
motion as Motion,
} from 'framer-motion';
import { forwardRef } from 'react';
const ReducedMotionDiv: ForwardRefComponent<
HTMLDivElement,
HTMLMotionProps<'div'>
> = forwardRef((props, ref) => {
const newProps = {
...props,
animate: undefined,
initial: undefined,
transition: undefined,
variants: undefined,
whileDrag: undefined,
whileFocus: undefined,
whileHover: undefined,
whileInView: undefined,
whileTap: undefined,
};
return <Motion.div {...newProps} ref={ref} />;
});
export const motion = new Proxy(Motion, {
get: (target, key) => {
if (key === 'div') {
return ReducedMotionDiv;
}
// @ts-expect-error - This is a proxy, so we can't be sure what the key is.
return target[key];
},
});
export {
AnimatePresence,
type Variants,
type HTMLMotionProps,
type MotionProps,
type TargetAndTransition,
type Transition,
type Spring,
} from 'framer-motion';
Yeh we really need this. I appreciate the attempt at sensible accessibility defaults when always
or user
is used -- but this options is also famously useful for testing, but only when it completely disables animations. MotionConfig
needs a new prop like reducedMotionMode
that can be set to all-animations
or something.
I'd also appreciate this option.
FYI: My case is not regarding testing, but to give users the option to turn off transitions completely in case their hardware isn't capable enough (also some people seem to just hate transitions).
@mattgperry Is this still being worked on by you, would accept contributions, – how can we make this happen?
For the purposes of disabling animations for visual regression tests, this worked for me in React. Perhaps it'll be useful for someone:
AnimationContext.ts that can be injected:
import { createContext } from "react";
const AnimationContext = createContext({
disableAnimations: false,
});
export default AnimationContext;
HOC wrapper for motion-div
. This uses a normal div when disableAnimations
is set to true
MotionDiv.ts:
import React, { useContext } from "react";
import { motion, HTMLMotionProps } from "framer-motion";
import AnimationContext from "./AnimationContext";
type MotionDivProps = React.PropsWithChildren<HTMLMotionProps<"div">>;
type DivProps = React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>>;
const MotionDiv: React.FC<MotionDivProps> = ({ children, ...props }) => {
const { disableAnimations } = useContext(AnimationContext);
if (disableAnimations) {
return <div {...(props as unknown as DivProps)}>{children}</div>;
}
return <motion.div {...props}>{children}</motion.div>;
};
export default MotionDiv;
At the root of the app (in my case, in nextjs _app.ts), wrap the tree with the context provider:
import AnimationContext from "../AnimationContext";
// or however you'd like to inject the value
const disableAnimations = !!process.env.NEXT_PUBLIC_DISABLE_ANIMATIONS;
<AnimationContext.Provider value={{ disableAnimations }}>
<MyApp />
</AnimationContext.Provider>
Now, in my components instead of import motion-div
I use MotionDiv
:
import MotionDiv from "./MotionDiv";
<MotionDiv
className="absolute -z-10 h-full w-full"
initial={{ scale: "95%" }}
animate={{ scale: "100%" }}
transition={{ duration: 3, ease: "circOut" }}
>
....
</MotionDiv>
This flag would also be great to support accessibility for users who prefers-reduced-motion
I'm surprised this hasn't been implemented yet. Given that accessibility, automated testing, dev mode (developers hate having to see animations for everything they test, it just gets in the way.) exist.
We use next.js server rendering. We resolved it by creating a wrapper around the motion
component. It falls back to plain elements if motion is disabled. We also applied the whileInView
styles to the element to replicate the "end" state of the animation. It is a simple and naive solution, but it works for us.
import {createElement} from 'react';
import {motion} from 'framer-motion';
export default new Proxy(
{},
{
get: (_, tagName) => {
return ({children, ...props}) => {
if (process.env.DISABLE_MOTION) {
if (!props.style) {
props.style = {};
}
if (props.whileInView) {
props.style = Object.assign(props.style, props.whileInView);
}
return createElement(tagName, props, children);
}
return createElement(motion[tagName], props, children);
};
},
}
);
Are you guys accepting PRs related to this? It's a very important and helpful feature. People have to come up with weird workarounds especially in SSR.
After almost 2 and half years, it's finally there 🙌
In 10.17.0
import { MotionGlobalConfig } from "framer-motion"
MotionGlobalConfig.skipAnimations = true
Is your feature request related to a problem? Please describe.
Hi all. We over at @getsentry use framer-motion (it's amazing!). We also do visual regression testing on our PRs, which means we generate screenshots from selenium based acceptance tests, then visually compare the screenshots against the screenshots generated from master.
For the screenshot comparisons, we have to make sure all animations are disabled. For framer motion We've added a
testableTransition
utility, which we then wrap all of ourtransition
values with. The idea is that it just sets the transitiontype
tofalse
essentially disabling the animation.This has generally worked, but we've noticed some places where the screenshots produce minorly different results between runs. You can see one here https://storage.googleapis.com/sentry-visual-snapshots/getsentry/sentry/6ad776199421d49c927f23c0a62877b96ad348e5/index.html. Maybe it is related to inconsistencies in how the browser renders elements on a gpu accelerated layer?
I was wondering if there was any better way to globally "disable" animations in framer motion.
Describe the solution you'd like
MotionConfig
could have an option to force framer motion to never "animate" anything, and always immediately transition states. This would be nicer than having to remember to always usetestableTransition
.Open to any ideas. Thanks for this incredible library!