Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does not seem to fully support styled-components? #194

Closed
rchrdnsh opened this issue Jul 13, 2019 · 25 comments
Closed

Does not seem to fully support styled-components? #194

rchrdnsh opened this issue Jul 13, 2019 · 25 comments
Labels
feature New feature or request

Comments

@rchrdnsh
Copy link

I want to do this, passing the gatsby-image component `Img into a styled component, while animating it, like so:

const AnimatedImage = styled(motion(Img))`
  width: 200px;
  height: auto;
`

but this does not seem to work, giving me this error:

TypeError: Object(...) is not a function
Module.<anonymous>
src/pages/index.js:81
  78 |   }
  79 | `
  80 | 
> 81 | const LaxImg = styled(motion(Img))`
  82 |   width: 200px;
  83 |   height: auto;
  84 | `

it seems to work with regular html tags, just not react components, which is no bueno for me :-(

Am i doing something wrong? Is something like this not supported? It really needs to be supported, so hopefully I am doing something wrong or it's on the way.

@rchrdnsh rchrdnsh added the feature New feature or request label Jul 13, 2019
@mattgperry
Copy link
Collaborator

mattgperry commented Jul 13, 2019 via email

@rchrdnsh
Copy link
Author

make sure you pass the ref prop from Img to the
underlying motion.div or whatever

hmmmmm...i don't quite follow...is there any documentation on this? Why would there be an underlying div? I would prefer just animating the image directly.

In react-spring I can simply do the following:

const AnimatedReactComponent = styled(animated(ReactComponent))`
  ...styles...
`

and then simply use that element in my JSX, which is exactly what I want to be able to do.

So I don't quite follow what you are saying in regards to using a ref in this case, although I do have a basic understanding of refs and useRef from the hooks documentation.

Is there any documentation on how to do this, or an example floating around somewhere?

@rchrdnsh
Copy link
Author

just to be clear, this works fine in framer-motion:

const MotionDiv = styled(animated.div)`
  ...styles...
`

...without seemingly any issues, just not this:

const AnimatedReactComponent = styled(animated(ReactComponent))`
  ...styles...
`

...which would be lovely if it did 😁

@mattgperry
Copy link
Collaborator

Here's an example of Styled Components working with motion.custom: https://codesandbox.io/s/framer-motion-with-styled-components-custom-components-i1wgk

@mattgperry
Copy link
Collaborator

Had a little bug - closing again!

@ooloth
Copy link

ooloth commented Jul 26, 2019

Awesome library. 🙌

For this particular issue, it would be great if this could work automatically (without the intermediate forwardRef step) since styled-components / emotion are so popular. 🙏

@jacobgranberry
Copy link

Here's an example of Styled Components working with motion.custom: https://codesandbox.io/s/framer-motion-with-styled-components-custom-components-i1wgk

I think I'm missing something - I don't see any instance in this example of either a component made with styled components or a component using motion.custom

@tpiersall
Copy link

tpiersall commented Aug 8, 2019

Here's an example of Styled Components working with motion.custom: https://codesandbox.io/s/framer-motion-with-styled-components-custom-components-i1wgk

I think I'm missing something - I don't see any instance in this example of either a component made with styled components or a component using motion.custom

Ya I can't see that either in the mentioned example

@joebourne
Copy link

@jacobgranberry @tpiersall This seems to show it a bit better: https://codesandbox.io/s/framer-motion-simple-animation-o32p0

@sbarry50
Copy link

Stumbled upon this... Doesn't look like a clear cut answer was posted. But I was able to get this to work. Think this is what @InventingWithMonster meant.

const AnimatedLink = styled(motion.custom(Link))`
    color: red;
`

styled(motion(Link)) threw the same error @rchrdnsh was getting.

@samajammin
Copy link

Thanks example @sbarry50 - I'm trying the same approach:

const Logo = styled(motion.custom(Img))`
   ...styles...
`

But getting caught up on passing the ref prop (or the forwardRef step @ooloth mentioned?). I'm seeing this error:

Error: No `ref` found. Ensure components created with `motion.custom` forward refs using `React.forwardRef`

Could someone please explain how that's accomplished? Thanks!

@sbarry50
Copy link

sbarry50 commented Feb 9, 2020

@samajammin it's pretty simple assuming Img is a component you can edit. Just follow the docs.

If you're trying to animate someone else's component and can't edit it like Gatsby's image component you can try wrapping it in a custom component and forwarding refs to that. Not 100% sure that would work (haven't tested it) but thats where I would start.

@sbarry50
Copy link

@samajammin Following up... not sure if the Img you're using there is a gatsby-image component but I just ran into this very issue with a Gatsby image and forwarding refs as shown in the React docs is not working for me either.

Seems like it could be related to this issue.

@samajammin
Copy link

@sbarry50 yup, it was a gatsby-image component. I found a way around my problem but I couldn't find a way to accomplish this either. I'll update here if I do!

@sbarry50
Copy link

sbarry50 commented Feb 11, 2020

@samajammin Cool, I just got mine working too. I just wrapped the image in a div and animated that instead. Annoying but only way I've figured out so far.

@migsan
Copy link

migsan commented Feb 13, 2020

I still quite don't understand how to Motion a Styled component. Does anyone have some examples?
I remember with Posed it was quite easy and you could style and then add pose on top of it for transitions. How is that achieved now with Framer?
Thanks in advance.

@designbyadrian
Copy link

@migsan What most of us had to do was to not try to multi-wrap the components:

const AnimatedImage = styled(motion.div)`
  img {
    width: 100%;
  }
`;

const variants = {} // ... etc

<AnimatedImage variants={variants}>
  <Img />
</AnimatedImage> 

This depends on what kind of transformation you wish to do to the image, of course.

@mattgperry
Copy link
Collaborator

@migsan For reference: A guide on how to use Framer Motion with Styled Components

https://inventingwithmonster.io/20200302-can-i-use-framer-motion-with-styled-components/

@reduxdj
Copy link

reduxdj commented Apr 11, 2020

I am baffled with this issue, while simple animations work, complex components do not, here's my on component...

import React, { useRef, useEffect } from 'react';
import styled from 'styled-components';
import { motion as Motion, useCycle } from 'framer-motion';
// Naive implementation - in reality would want to attach
// a window or resize listener. Also use state/layoutEffect instead of ref/effect
// if this is important to know on initial client render.
// It would be safer to  return null for unmeasured states.
export const useDimensions = ref => {
  const dimensions = useRef({ width: 0, height: 0 });

  useEffect(() => {
    dimensions.current.width = ref.current.offsetWidth;
    dimensions.current.height = ref.current.offsetHeight;
  }, [ref]);

  return dimensions.current;
};

const Path = props => <Motion.path fill="transparent" strokeWidth="3" stroke="hsl(0, 0%, 18%)" strokeLinecap="round" {...props} />;

const MENU_ITEM_VARIANTS = {
  open: {
    y: 0,
    opacity: 1,
    transition: {
      y: { stiffness: 1000, velocity: -100 },
    },
  },
  closed: {
    y: 50,
    opacity: 0,
    transition: {
      y: { stiffness: 1000 },
    },
  },
};

const colors = ['#FF008C', '#D309E1', '#9C1AFF', '#7700FF', '#4400FF'];

export const MenuItem = ({ i, children }) => {
  const style = { border: `2px solid ${colors[i]}` };
  return (
    <Motion.div variants={MENU_ITEM_VARIANTS}>
      <div className="icon-placeholder" style={style} />
      {children}
    </Motion.div>
  );
};

const ACCORDION_VARIANTS = {
  open: {
    transition: { staggerChildren: 0.07, delayChildren: 0.2 },
  },
  closed: {
    transition: { staggerChildren: 0.05, staggerDirection: -1 },
  },
};

export const AccordionPanel = ({ children }) => (
  <Motion.div variants={ACCORDION_VARIANTS}>
    {React.Children.toArray(children).map((item, i) => (
      <MenuItem i={i} key={i}>
        {item}
      </MenuItem>
    ))}
  </Motion.div>
);

const sidebar = {
  open: (height = 1000) => ({
    clipPath: `circle(${height * 2 + 200}px at 40px 40px)`,
    transition: {
      type: 'spring',
      stiffness: 20,
      restDelta: 2,
    },
  }),
  closed: {
    clipPath: 'circle(30px at 40px 40px)',
    transition: {
      delay: 0.5,
      type: 'spring',
      stiffness: 400,
      damping: 40,
    },
  },
};

export const MenuToggle = styled(({ className, toggle }) => (
  <button className={className} onClick={toggle}>
    <svg width="23" height="23" viewBox="0 0 23 23">
      <Path
        variants={{
          closed: { d: 'M 2 2.5 L 20 2.5' },
          open: { d: 'M 3 16.5 L 17 2.5' },
        }}
      />
      <Path
        d="M 2 9.423 L 20 9.423"
        variants={{
          closed: { opacity: 1 },
          open: { opacity: 0 },
        }}
        transition={{ duration: 0.1 }}
      />
      <Path
        variants={{
          closed: { d: 'M 2 16.346 L 20 16.346' },
          open: { d: 'M 3 2.5 L 17 16.346' },
        }}
      />
    </svg>
  </button>
))`
  background: red;
`;

const variants = {
  open: {
    transition: { staggerChildren: 0.07, delayChildren: 0.2 },
  },
  closed: {
    transition: { staggerChildren: 0.05, staggerDirection: -1 },
  },
};

export const Navigation = () => (
  <Motion.div variants={variants}>
    {[0, 1, 2, 3, 4].map(i => (
      <MenuItem i={i} key={i} />
    ))}
  </Motion.div>
);

export const Accordion = ({ children, className }) => {
  const [isOpen, toggleOpen] = useCycle(false, true);
  const containerRef = useRef(null);
  const { height } = useDimensions(containerRef);
  return (
    <Motion.div className={className} initial={false} animate={isOpen ? 'open' : 'closed'} custom={height} ref={containerRef}>
      <Motion.div className="background" variants={sidebar} />
      <MenuToggle toggle={() => toggleOpen()} />
      <AccordionPanel children={children} />
    </Motion.div>
  );
};

If I remove the accordionPanel component my menu toggle has a background, if I add it back, the Toggle loses it's style. I'm completely baffled.

Usage:

<Accordion>
<SomeThing />
</Accordion>

@Snikerso
Copy link

Snikerso commented Jun 9, 2020

It working !

import React from 'react';
import { ReactComponent as PuzzlesSVG } from 'assets/puzzle.svg';
import styled from 'styled-components';
import { motion } from 'framer-motion'

const PuzzlesStyle = styled(motion.custom(PuzzlesSVG))`

.a{

}

`;

const Puzzles = () => {

return (
    <PuzzlesStyle
        initial={{ x:0}}
        animate={{ x:20 }}
        transition={{ duration: 3, loop: Infinity }}
        whileHover={{ scale: 1.1 }} />
)

}
export default Puzzles

@RectoVersoDev
Copy link

RectoVersoDev commented Jul 8, 2020

Hello, I can't get this working with gatsby Link component :(

I'm getting this error :

TypeError: can't access property "custom", framer_motion__WEBPACK_IMPORTED_MODULE_2__.default is undefined

I tried these syntaxes:

const Button = styled(motion.custom(Link))`
    color: red;
`

&

const Button = motion.custom(styled.Link`
    color: red;
`);

Someone have any tips?

@Jojocaster
Copy link

Hello, I can't get this working with gatsby Link component :(

I'm getting this error :

TypeError: can't access property "custom", framer_motion__WEBPACK_IMPORTED_MODULE_2__.default is undefined

I tried these syntaxes:

const Button = styled(motion.custom(Link))`
    color: red;
`

&

const Button = motion.custom(styled.Link`
    color: red;
`);

Someone have any tips?

I'm afraid Link isn't a styled-component - it's an actual React component, just wrap it inside your own styled-component :)

@unbeatendev
Copy link

Okay I just figured this it now. You can use this instead.

Const styledImage = styled(motion.custom(props => <Img {...props}/>))

then add your styles.

once you are then then you can do this.

<StyledImage fluid{fluid.childImageSharp.fluid} here you add your framer motion scripts />

that’s all. It works perfectly now.

@unbeatendev
Copy link

Hello, I can't get this working with gatsby Link component :(
I'm getting this error :
TypeError: can't access property "custom", framer_motion__WEBPACK_IMPORTED_MODULE_2__.default is undefined
I tried these syntaxes:

const Button = styled(motion.custom(Link))`
    color: red;
`

&

const Button = motion.custom(styled.Link`
    color: red;
`);

Someone have any tips?

I'm afraid Link isn't a styled-component - it's an actual React component, just wrap it inside your own styled-component :)

I provided a solution to this. You can pass your Link props instead. It will work.

@RectoVersoDev
Copy link

RectoVersoDev commented Jan 7, 2021

@OginniSamuel Thanks for this but it's not working with the Gatsby Link component :(

I get a critical error: "React does not recognize the whileHover prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase whilehover instead. If you accidentally passed it from a parent component, remove it from the DOM element."

initial, whileHover and animate props are added in the html and animations don't work

I do this:

const ContactLink = styled(motion.custom(props => <Link {...props} />))`
 ...styles
`
<ContactLink
   to="/contact"
   initial="rest"
   whileHover="hover"
>
   <span>Contact</span>
   <motion.img
     src={loudSpeaker}
     variants={contactLinkVariants}
   />
</ContactLink>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request
Projects
None yet
Development

No branches or pull requests