motiondivision / motion

A modern animation library for React and JavaScript
https://motion.dev
MIT License
24.55k stars 824 forks source link

[BUG] Not working on IE #364

Closed mackan92 closed 3 years ago

mackan92 commented 5 years ago

The website my team are developing is using Framer Motion. It seems that it won't work on Internet Explorer. I've tested it with IE 11. Searching for bug reports lead me to nothing.

When I tried to go in to your website on IE, https://www.framer.com/motion/, animations did not appear to be working either.

Has Framer Motion support for IE? If no, do you plan to implement it in a near future?

mattgperry commented 5 years ago

No support for IE11 now or ever I'm afraid. Time-permitting I hope to have a browser support list up in the docs, though it's basically all modern browsers.

msheakoski commented 5 years ago

I wouldn't bother supporting IE11, it's a dead end. The new Chromium-based Edge supports Windows 7 and newer. I'm sure Microsoft will be pushing it heavily to existing IE users via updates, notifications, banners on their web properties, and as a default in new installs of Windows. For corporate users, there is an "IE mode" built in for legacy sites.

thebuilder commented 5 years ago

Seems like the issue on frame.com/motion is caused by arrow functions - Is the problem you are using any methods in the library that can't be polyfilled?

msheakoski commented 5 years ago

@thebuilder I had to support IE11 on a project that used Pose, so I imagine the polyfills needed are similar. Here is what made IE11 work:

import "core-js/features/array/find";
import "core-js/features/object/assign";
import "core-js/features/promise";
import "core-js/features/string/ends-with";
import "core-js/features/symbol/for";
import "core-js/features/weak-set";
mackan92 commented 5 years ago

Thank you for your answers. My project also has to support IE. I added the polyfills and managed to make Framer Motion partly work on IE, but there were still a lot of issues. I will look at another option, probably remove animations completely on IE and keep Framer Motion for the modern browsers.

thebuilder commented 5 years ago

@mackan92 any information about what/how you added the polyfills? What issues are you seeing? Broken animations, or script errors? Are you loading the the ES version of Framer Motion, or the ES5 version?

https://unpkg.com/browse/framer-motion@1.6.14/dist/

Most likely Webpack is loading the es version and just uses that uncompiled.

thebuilder commented 5 years ago

https://github.com/react-spring/react-spring/ has had the same issue with shipping ES6 bundle - They solved it by having you import the .cjs file instead.

anilanar commented 5 years ago

If it's just a polyfill problem, I highly suggest everyone to use babel-preset-env or babel-plugin-transform-runtime with useBuiltIns and to not ignore node_modules, except a few like react-dom. You will thank me later.

samuelgoddard commented 4 years ago

Did anyone figure out how to get this working with Gatsby? Or could anyone point me in the right direction for how I could potentially support IE11 with Framer Motion / Gatsby. Thank you!

designbyadrian commented 4 years ago

@samuelgoddard Gatsby shouldn't have any impact on wether Framer Motion works with IE11. Maybe you issue is a combination of three: 1) Framer Motion with IE11, 2) using JS polyfills with Gatsby, 3) Framer Motion page transitions in Gatsby.

samuelgoddard commented 4 years ago

@designbyadrian thanks for the reply, I don't really know where to start with debugging - I'm getting Object doesn't support property or method 'append' i IE11 when using framer motion page transitions, I've tried polyfilling it using polyfill.io but I then get an Object doesn't support this action error. Do you have any idea at all? Example site can be found here - https://prb-architects.netlify.app/

mattgperry commented 4 years ago

I keep meaning to add explicit docs about this to the repo but Motion 2 doesn’t support IE11 and Proxy is impossible to polyfill. I might be able to add an extra entry point like import { ieMotion as motion } where you accept ~1.5kb extra bundle size in exchange for IE11 support?

On Sun, 26 Jul 2020 at 15:17, Sam Goddard notifications@github.com wrote:

@designbyadrian https://github.com/designbyadrian thanks for the reply, I don't really know where to start with debugging - I'm getting Object doesn't support property or method 'append' i IE11 when using framer motion page transitions, I've tried polyfilling it using polyfill.io but I then get an Object doesn't support this action error. Do you have any idea at all? Example site can be found here - https://prb-architects.netlify.app/

— You are receiving this because you commented.

Reply to this email directly, view it on GitHub https://github.com/framer/motion/issues/364#issuecomment-663987020, or unsubscribe https://github.com/notifications/unsubscribe-auth/AB34WKTDG4IQCGWIAFY2J5LR5QUHTANCNFSM4JA5LZTA .

GooBall commented 4 years ago

@InventingWithMonster That would probably be the best of both worlds. I'm in a position where the product I'm working on is not going to be able to drop support for IE11 until 2021 at the earliest.

For future me, @samuelgoddard and anyone else Googling... even if you are using CRA or react-app-polyfill but have upgraded to framer-motion v2, you will be getting the IE console error of 'Proxy' is undefined in some chunk.js file and Object doesn't support property or method 'append'.

As Matt says, V2 doesn't work with IE so downgrading to framer-motion@1.10.3 should get you back on track.

brentg5 commented 4 years ago

Thank you for working on this @InventingWithMonster! As much as I'd love to sunset IE11, we are also in a similar state and need to support it for a while. Looking forward to seeing what you come up with.

greypants commented 4 years ago

I might be able to add an extra entry point like import { ieMotion as motion } where you accept ~1.5kb extra bundle size in exchange for IE11 support?

@InventingWithMonster☝️ That would be awesome.

Sadly, IE 11 support is critical for us. We're an enterprise company with a surprising number of clients with a death grip on IE 11. We love framer motion ❤️, and would love continued support. We'll pay the extra 1.5kb gladly.

Will roll back to 1.x.x for now.

tolesdev commented 4 years ago

@InventingWithMonster Any progress with IE11 support?

barchenaqaunt commented 4 years ago

Hey, I'm using version 2.6.13, when I'm loading my app in IE 11 the browser crash and I get an error - "Proxy is undefined". did anyone encounter this problem and found a way to deal with it? Lowering the version to 1.x.x isn't an option for me

brentg5 commented 4 years ago

@InventingWithMonster Is there any chance we could reopen this issue? The op closed it, but clearly there is a lot of critical interest in IE11 support, even if it results in a larger bundle size. Without it, many of your enterprise consumers will be put in a tough position.

mattgperry commented 4 years ago

Yep this is worth keeping open imo, I will manage to take a look in the coming weeks

mattgperry commented 4 years ago

The linked PR ^ shows a new API that will help you create a replacement for motion that doesn't use Proxy. Please note that this doesn't constitute IE11 support, for instance future versions of Motion may require new polyfills, or make others unnecessary.

However this is a nicer solution IMO than a separate legacy entry point, both in terms of code complexity on our side (= no dusty API corners to break for you) and also it will actually be slightly smaller than importing motion itself.

thebuilder commented 4 years ago

This is great - How do you envision it actually being used? Should we create all the HTML tag's we'd want to support in the custom motion and share that instance throughout the application?

import { createDomMotionComponent } from 'framer-motion'

/** IE 11 supported version of the motion component */
export const motion = { 
  div: createDomMotionComponent("div"),
  span: createDomMotionComponent("span") ,
  svg: createDomMotionComponent("svg") ,
}
mattgperry commented 4 years ago

Yeah exactly this ^

thebuilder commented 4 years ago

Just to improve upon it, a check for Proxy support could be added, so it only uses the fallback if needed:

Not recommend - See below ```js import { motion, createDomMotionComponent } from 'framer-motion /** IE 11 supported version of the motion component */ export const motion = typeof Proxy === 'undefined' ? { div: createDomMotionComponent("div"), span: createDomMotionComponent("span") , svg: createDomMotionComponent("svg") , } : motion; ```
mattgperry commented 4 years ago

I would recommend against that approach as Motion's motion isn't necessarily the same as the user-generated one leading to obscure runtime errors.

You'd be writing in an environment with Proxy, so your module would export motion. So you go ahead and use motion.header or motion.custom(Component), but neither of these have been implemented. You ship it, it works in all testing environments, but it'd only be someone using it in IE11 who would get the runtime error. It's probably safer to use your custom motion component always, so if you use an undefined component it breaks for you in dev.

thebuilder commented 4 years ago

Tried it on our production website, and it's working great. Did have to modify the typescript types to get them to match, since it createDomMotionComponent doesn't match the exported motion types.

// createDomMotionComponent
ForwardRefExoticComponent<MotionProps & React.RefAttributes<unknown>>

// motion.div type
ForwardRefComponent<UnwrapFactoryElement<ReactHTML[K]>, HTMLMotionProps<K>>

The result is the returned type doesn't contain the HTMLProps. Could it make sense to reuse the UnwrapFactoryElement and UnwrapSVGFactoryElement types to create the correct types? The function gets the DOM element as a string, so should be possible to map the same way.

I've generated my own list of ValidMotionTypes by reusing the exported HTMLMotionComponents. Could be optimized further, but works for now.

import React from 'react';
import {
  createDomMotionComponent,
  motion as originalMotion,
} from 'framer-motion';
import { CustomDomComponent } from 'framer-motion/types/render/dom';
import {
  HTMLMotionComponents,
  SVGMotionComponents,
} from 'framer-motion/types/render/dom/types';

type CustomMotionType = {
  custom: <Props>(
    Component:
      | string
      | React.ComponentClass<Props, any>
      | React.FunctionComponent<Props>,
  ) => CustomDomComponent<Props>;
};

type ValidMotionTypes = CustomMotionType &
  Pick<
    HTMLMotionComponents & SVGMotionComponents,
    // List all the exported HTML/SVG tags here. This ensures
    'div' | 'span' | 'button' | 'a' | 'article' | 'header' | 'svg' | 'path'
  >;

export const motion = {
  div: createDomMotionComponent('div'),
  span: createDomMotionComponent('span'),
  button: createDomMotionComponent('button'),
  a: createDomMotionComponent('a'),
  article: createDomMotionComponent('article'),
  header: createDomMotionComponent('header'),
  svg: createDomMotionComponent('svg'),
  path: createDomMotionComponent('path'),
  custom: originalMotion.custom,
} as ValidMotionTypes;

// re-export everything
export * from 'framer-motion';
linusklingzell commented 4 years ago

Tried this fix/solution in a clean CRA with polyfills for IE11 but can't make this to work. As soon as I import import { createDomMotionComponent } from 'framer-motion' without even using it, I get an error that Proxy is not defined when running the code in IE11 browser. Is there something else more than correct polyfills to make this to work?

mattgperry commented 4 years ago

@linusklingzell Have you enabled code splitting in your bundler?

anzorb commented 4 years ago

@mattgperry I am guessing this doesn't polyfill motion.custom? I see the createMotionProxy function being bundled in, even with above workaround. I am also using CRA + ie11 polyfills.

Cheers!

linusklingzell commented 4 years ago

Tried code splitting as you suggested @mattgperry with dynamic imports but I still get the same error

rr-justin-hanselman commented 4 years ago

Hello,

I've just run into this problem. Chakra-ui uses the new framer-motion but we still want to support IE11 until it is no longer profitable.

My plan is to resolve my module to a wrapper that can export a non-proxied equivalent of motion if I detect the need to like @thebuilder suggested.

However, I was wondering why the index-legacy.ts was removed: https://github.com/framer/motion/commit/b4319c78fb4bde28ce0d2a8008df48d7e3fd1c8b

Rather than having to keep a separate list of modules, I would like to import the defaults and custom, which seemed to be exposed previously, but is now removed. Could you maybe explain why this functionality is gone or its downsides? @mattgperry

Thanks!

mattgperry commented 4 years ago

Reopening as there's clearly some further things to figure out

rr-justin-hanselman commented 4 years ago

After working on it yesterday, I was able to get the wrapper to work, with the current framer-motion package.

The wrapper file (client/framer-motion-wrapper.js in my solution):

import { motion as origMotion, createDomMotionComponent } from '../node_modules/framer-motion';
import { proxyDefined } from './clientState';

// fragile copied from internal framer-motion supported-elements.ts
import { htmlElements, svgElements } from './framer-motion-supported-elements';

// Adapted from https://github.com/framer/motion/commit/b4319c78fb4bde28ce0d2a8008df48d7e3fd1c8b
const createNonProxyMotion = () => {
  let motionProxy = {
    custom: (component) => {
      createDomMotionComponent(component);
    },
  };
  motionProxy = htmlElements.reduce((acc, key) => {
    acc[key] = createDomMotionComponent(key);
    return acc;
  }, motionProxy);
  motionProxy = svgElements.reduce((acc, key) => {
    acc[key] = createDomMotionComponent(key);
    return acc;
  }, motionProxy);
  return motionProxy;
};

export const motion = proxyDefined ? origMotion : createNonProxyMotion();

export * from '../node_modules/framer-motion';

Note, because motion calls to Proxy on module initialization, you STILL need a proxy-polyfill before your module-wrapper, I have a main polyfill that is included at the top of my entry-point with this logic:

// determine the state of the client before polyfilling
import { proxyDefined } from './clientState';

// ...Other polyfills

// Example: immer assumes es6 if polyfill is defined - keep this in mind if other libraries are failing after polyfilling
import { enableES5 } from 'immer';
// We have to include this for now, since framer-motion requires the Promise call immediately
import 'proxy-polyfill';

if (!proxyDefined) enableES5();

The clientState.js is basically a copy-paste of the proxy-polyfill switch:

const hasProxy = (scope) => !!scope.Proxy;

// Using the same Proxy polyfill checks as proxy-polyfill
export const proxyDefined = hasProxy((typeof process !== 'undefined'
&& {}.toString.call(process) === '[object process]')
|| (typeof navigator !== 'undefined' && navigator.product === 'ReactNative')
  ? global
  : self);

Finally, whatever your packing solution, you will want to perform a module replacement. Here's the example with webpack:

    config.plugins.push(new webpack.NormalModuleReplacementPlugin(
      /^framer-motion$/, path.resolve(__dirname, 'client/framer-motion-wrapper.js'),
    ));

Hopefully this helps in both the bug-fixing and anyone else hoping to still support IE11. If you find a better solution, I would love to hear!

masaroli commented 3 years ago

Greetings! I've started a small project with framer motion. I didn't know it doesn't support IE. Tried using createDomMotionComponent instead of motion but the animations go into a loop and I couldn't figure out why. With motion works fine but the fix doesn't. Also, the types don't match as @thebuilder commented.

mackan92 commented 3 years ago

@rr-justin-hanselman I have tried to do exactly as you and I have managed to make the wrapper work. Unfortunately, I am still getting "'Proxy' is undefined" on IE. The polyfill you are using, 'proxy-polyfill', is it this one? https://www.npmjs.com/package/proxy-polyfill

Update: Now I managed to import the polyfill mentioned above correctly. It works with IE 11! I imported the polyfill like this: import 'proxy-polyfill/proxy.min.js';

I use the latest version of Framer Motion (2.9.4).

bernhardw commented 3 years ago

@mackan92 Would you mind posting the files and changes you have made to make it work in IE 11? The polyfill works, but I'm having troubles copying the additional files (framer-motion-supported-elements.ts, clientState.ts etc.) together.

katiawheeler commented 3 years ago

I can confirm, I'm still getting this error on IE 11 using the provided createDomMotionComponent - the proxy-polyfill is also not working for me 😞

rr-justin-hanselman commented 3 years ago

@mackan92 - nice!

@bernhardw - I probably should've clarified -

clientState.ts should be your own file in your library that you import in your project at the top of whatever file you're using to import all your necessary polyfills. The reason for this is that certain other libraries already detect if proxy is in on the window and change their implementation (in my case, immer was doing this automatically so I had to keep proxy-polyfill unimplemented until later). You use the clientState.ts to simply get all the info about the client before you start importing things that will change the window.

As for the frame-motion-supported-elements.ts, those were literaly copy-pasted from the framer-motion repo into a file on my project (they are not publicly exposed right now from what I can see). Do a search in the repo for the file I've commented above that line and you should find it.

Hope this helps!

mattgperry commented 3 years ago

I've added sideEffects: false to the package.json in the latest versions which should aid code-splitting. Is it possible for one of you who had trouble with createDomMotionComponent still triggering the Proxy error to try with 3.5.3?

klaasman commented 3 years ago

Hi Matt,

I'm running a nextjs application where I don't notice any difference between importing v3.5.2 and v3.5.3: both versions result in the proxy error in IE11 while only the createDomMotionComponent is imported.

By the way, to me it looks like importing the proxy-polyfill works.

mattgperry commented 3 years ago

I'm closing this now as we should have fixed code-splitting in Webpack with 3.8.0. Please reopen if you are using ES modules and this still doesn't work.

pleunv commented 3 years ago

I'm honestly a bit lost when reading through this issue and I can't immediately find docs about this, so apologies for bumping this. Am I right in assuming that this approach outlined earlier by @thebuilder should allow for IE11 compatibility today after the fix in 3.8?

This is great - How do you envision it actually being used? Should we create all the HTML tag's we'd want to support in the custom motion and share that instance throughout the application?

import { createDomMotionComponent } from 'framer-motion'

/** IE 11 supported version of the motion component */
export const motion = { 
  div: createDomMotionComponent("div"),
  span: createDomMotionComponent("span") ,
  svg: createDomMotionComponent("svg") ,
}

I'm guessing this also still depends on Proxy being polyfilled?

The part that confuses me the most is that this now seems to be fixed with the code-splitting added in 3.8, can anyone elaborate on how the lack thereof was breaking IE11 compatibility? Mostly out of curiosity :)

mattgperry commented 3 years ago

@pleunv the idea with the code-splitting is you shouldn’t need a polyfill for Proxy any more as, if it works successfully, the code that uses Proxy won’t be included. I think a guide is a good idea but the method you outlined is what I had in mind

Edit: to address your last question, it was IE11 even encountering Proxy that breaks it, which is why this requires code splitting in Webpack to fix.

pleunv commented 3 years ago

Totally clarifies it, thank you!

hanseltime commented 3 years ago

Hey @mackan92,

Could you elaborate on this code-splitting? I have version 4.1.17, but looking at the code here, the actual framer-motion module calls proxy during initialization code.

To my understanding, that should mean I need a proxy polyfill so that the framer-motion module doesn't fail.

Note: I have a working example of wrapping this module for that logic, but it's more complicated than just having a switch in framer-motion: https://github.com/hanseltime/stylis-ie11-plugins/tree/master/examples/chakra-app

If I'm missing a document or method for ensuring this "@PURE" function is split out completely, I'd love to be enlightened!

Thanks

thathurtabit commented 3 years ago

Hopefully I'm not missing any documentation on this, but for our needs we're OK with not having FramerMotion animate on IE11, but would it cause errors in IE11? Would I need to do a browser check for IE11 and provide a different (basic) component for those users? Thanks.

kwanso-awais commented 1 year ago

This is framer motion transition is working fine locally but too jagged on live eCommerce store. Can you please help me. Local Transition You can check below video link. Transition is working fine here but not on live server https://www.loom.com/share/9b6268f8de014a14ae2f50fa5716320a