Search code examples
reactjsanimationstyled-componentsreact-props

Passing a direction prop into styled-components keyframe component?


I'm trying to implement a reusable text animation component where a direction prop can be passed in. I probably close but doesn't seem to be working. Also open to better ways of implementing it a better way.

import React from "react"
import styled from "styled-components"
import { GlobalStyles } from "./global"
import TextAnimations from "./Components/TextAnimations"

const Container = styled.div`
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
`
const NavBar = styled.nav`
  background: #3a3a55;
  padding: 0.25rem;
  width: 100%;
  height: 10vh;
`
const Main = styled.main`
  background: #3a3a55;
  color: white;
  padding: 0.25rem;
  flex: 10 1 auto;
  height: 100vh;
`

const Break = styled.div`
  flex-basis: 100%;
  width: 0;
`

function App() {
  return (
    <>
      <GlobalStyles />
      <Container>
        <NavBar>NavBar</NavBar>
        <Break />
        <Main>
          <TextAnimations text='Sample text from the left' direction='left' />
          <TextAnimations text='Sample text from the right' direction='right' />
        </Main>
      </Container>
    </>
  )
}

export default App

and then the animation component:

import { motion } from "framer-motion"
import styled, { keyframes } from "styled-components"

type TextAnimationProps = {
  text: string
  direction: string
}

const Left = keyframes`
  0% { left: -3.125em; }
  100% { left: 3em;}
`

const Right = keyframes`
  0% { Right: -3.125em; }
  100% { Right: 3em;}
`

const HomeHeader = styled.div`
  h1 {
    font-weight: lighter;
  }
  position: relative;
  top: 0;
  animation: ${(props) => (props.defaultValue === "left" ? Left : Right)} // is this right?
  animation-duration: 3s;
  animation-fill-mode: forwards;
`

const TextAnimations = ({ text, direction }: TextAnimationProps) => {
  return (
    <HomeHeader defaultValue={direction}>
      <h1>{text}</h1>
    </HomeHeader>
  )
}

export default TextAnimations

I'd also like to make it more flexible so that I can add 'top', 'bottom', etc.

Is this the best way to handle text animations?


Solution

  • You can create a separate function to set the animation. The function will receive the props of the styled component from which the function will access the direction prop.

    const setHomeHeaderAnimation = ({ direction = "left" }) => {
      const animation = keyframes`
        0% {
          ${direction}: -3.125em; 
        }
        100% { 
          ${direction}: 3em;
        }
      `;
        
      return css`
        position: relative;
        animation: ${animation};
        animation-duration: 3s;
        animation-fill-mode: forwards;
      `;
    };
    
    const HomeHeader = styled.div`
      ${setHomeHeaderAnimation}
    
      h1 {
        font-weight: lighter;
      }
    `;
    
    const App = () => (
      <div>
        <HomeHeader>
          <div>Some text</div>
        </HomeHeader>
        <HomeHeader direction="right">
          <div>Some text</div>
        </HomeHeader>
        <HomeHeader direction="top">
          <div>Some text</div>
        </HomeHeader>
        <HomeHeader direction="bottom">
          <div>Some text</div>
        </HomeHeader>
      </div>
    );