framer / motion

Open source, production-ready animation and gesture library for React
https://framer.com/motion
MIT License
23.78k stars 808 forks source link

[BUG] `Cannot set properties of undefined (setting 'transform')` when mixing 3D and setting `layout` on DOM element #2318

Closed Pinpickle closed 9 months ago

Pinpickle commented 1 year ago

1. Read the FAQs 👇

✅

2. Describe the bug

With a specific configuration of 3D and 2D elements, re-rendering a DOM element with layout throws the error Cannot set properties of undefined (setting 'transform').

3. IMPORTANT: Provide a CodeSandbox reproduction of the bug

https://codesandbox.io/s/prod-water-6gy983?file=/src/App.tsx

@jaredkhan did all the hard work on making this repro <3

Stack trace:

resetTransform
framer-motion/dist/cjs/index.js:4243:34
ProjectionNode.resetTransform
framer-motion/dist/cjs/index.js:3252:17
resetTransformStyle
framer-motion/dist/cjs/index.js:4146:10
FlatTree.forEach
framer-motion/dist/cjs/index.js:2770:23
ProjectionNode.didUpdate
framer-motion/dist/cjs/index.js:3143:24
MeasureLayoutWithContext.componentDidUpdate
framer-motion/dist/cjs/index.js:4494:29

4. Steps to reproduce

Steps to reproduce the behavior:

  1. Go to the Codesandbox: https://codesandbox.io/s/prod-water-6gy983?file=/src/App.tsx
  2. Click on the red box
  3. See error thrown

5. Expected behavior

The red box should animate from one side to the other.

Explanation

This code reproduces the error:

import { Canvas } from "@react-three/fiber";
import { motion as motion3 } from "framer-motion-3d";
import { useMotionValue, motion } from "framer-motion";
import { useState } from "react";

export default function App() {
  // If the motion value is 0, it doesn't break
  const motionVal = useMotionValue(1);
  const [onLeft, setOnLeft] = useState(true);

  return (
    <>
      <div
        style={{
          zIndex: 5,
          position: "absolute",
          display: "flex",
          justifyContent: onLeft ? "flex-start" : "flex-end",
          background: "grey",
          width: "300px",
          height: "200px"
        }}
        onClick={() => setOnLeft((val) => !val)}
      >
        <motion.div style={{ background: "red" }} layout>
          Click, I should animate left/right
        </motion.div>
      </div>
      <motion.div style={{ width: "100%", height: "100%" }}>
        <Canvas>
          <motion3.group position={[0, motionVal, 0]}>
            <motion3.mesh />
          </motion3.group>
        </Canvas>
      </motion.div>
    </>
  );
}

Here is the magical set of requirements:

From what I can tell, the 3D motion element (<motion.group>) is creating an HTMLProjectionNode for some reason. So when this line is called on the re-render:

https://github.com/framer/motion/blob/cebd3c3b7fcf404b3733bbea49ec35bdd6f0b7e3/packages/framer-motion/src/projection/node/HTMLProjectionNode.ts#L24

instance is a THREE.Group which does not have a style property and it crashes

AlexanderMoroz commented 1 year ago

Ok, confirm. Have same issue with <motion.div layout> and motion.group

hatsumatsu commented 11 months ago

Can confirm. This kills using LayoutAnimation in the DOM and framer-motion-3d in the same view.