cclloyd
cclloyd

Reputation: 9205

JSS keyframes not working when passing props

I have a Spinner component that's basically a loading icon. I'm trying to pass props to the JSS styles so that it can be customized. But the animations don't seem to work if I pass props to the keyframes.

Below is the component. When I use the animation $spinnertest it works fine. If I use $spinners, it doesn't load the animation (when inspecting the elements, animation-name doesn't even show up in the class, leading me to believe it doesn't get generated. ).

**Example CodeSandBox of issue (just change animation to spinners): https://codesandbox.io/s/exciting-shirley-pqt1o?fontsize=14&hidenavigation=1&theme=dark

const useStyles = makeStyles(theme => ({
    root: props => ({
        width: props.size,
        height: props.size,
        position: 'relative',
        contain: 'paint',
        display: 'inline-block',
    }),
    spinner: props => ({
        width: props.size*0.3125,
        height: props.size*0.3125,
        background: props.color,
        position: 'absolute',
        animationDuration: props.duration,
        animationIterationCount: 'infinite',
        animationTimingFunction: 'ease-in-out',
    }),
    spinnerAnimation: {
        animationName: '$spinners',
    },
    square2: props => ({
        animationDelay: -props.duration/2,
    }),
    '@keyframes spinnertest': {
        '25%': {
            transform: 'translateX(22px) rotate(-90deg) scale(.5)',
        },
        '50%': {
            transform: 'translateX(22px) translateY(22px) rotate(-180deg)',
        },
        '75%': {
            transform: 'translateX(0) translateY(22px) rotate(-270deg) scale(.5)',
        },
        'to': {
            transform: 'rotate(-1turn)',
        },
    },
    '@keyframes spinners': props => ({
        '25%': {
            transform: `translateX(${props.translate}px) rotate(-90deg) scale(.5)`,
        },
        '50%': {
            transform: `translateX(${props.translate}px) translateY(${props.translate}px) rotate(-180deg)`,
        },
        '75%': {
            transform: `translateX(0) translateY(${props.translate}px) rotate(-270deg) scale(.5)`,
        },
        'to': {
            transform: `rotate(-1turn)`,
        },
    }),
}));

export default function Spinner(props) {
    const {duration, size, color} = props;
    const classes = useStyles({
        duration: duration,
        size: size,
        color: color,
        translate: size*(1-0.3125),
    });

    return (

        <Box className={classes.root}>
            <Box className={clsx(classes.spinner, classes.spinnerAnimation)} />
            <Box className={clsx(classes.spinner, classes.square2, classes.spinnerAnimation)} />
        </Box>
    )

}

Spinner.defaultProps = {
    duration: 1800,
    size: 32,
    color: #fff,
}

Upvotes: 2

Views: 1001

Answers (2)

amirhe
amirhe

Reputation: 2341

It sounds that MUI has a bug around props in makeStyles @keyframes #16673 as Olivier Tassinari stated, this bug will be fixed in v5 where MUI gonna use a new styling solution styled-components RCF #22342.

The problem is even more general:
The arrow functions (with or without props) do not work within makeStyles #21011

Passing the props to rules in your defined keyframes will fix it (after v5 has been available, hopefully)

"@keyframes spinners": {
    "25%": {
      transform: (props) =>
        // console.log(props) and template generation will be created correctly.
        `translateX(${props.translate}px) rotate(-90deg) scale(.5)`
    },
    // ...
  }

Until then you can use higher-order useStyle creator for embedding your keyframes, as @buzatto suggested.

Or define your animation presets in your theme object and uses them globally around your project.

const theme = createMuiTheme({
  animation: {
    presets: {
      duration: 180,
      // or even function
      rotateDeg: (angle) => `{angle}deg`
      //...
    }
  }
});


// usage
const useStyles = makeStyles(theme => ({
  "@keyframes spinners": {
    "25%": {
      transform: `translateX(${
        theme.animation.presets.duration * 10
      }px) rotate(${theme.animation.presets.rotateDeg(-90)}) scale(.5)`,
    },
  },
}

Upvotes: 1

buzatto
buzatto

Reputation: 10382

I have a turnaround solution, which works (not that pretty). You would turn your withStyles into a currying function, that takes keyframesProps, and at your key frame definition you would use an IIFE that returns the object with its properties:

const useStyles = keyframesProps => makeStyles((theme) => ({
   ... all other styles,
   // you need to call an IIFE because keyframes doesn't receive a function
  "@keyframes spinners": ((props) => ({
    "25%": {
      transform: `translateX(${props.translate}px) rotate(-90deg) scale(.5)`
    },
    "50%": {
      transform: `translateX(${props.translate}px) translateY(${props.translate}px) rotate(-180deg)`
    },
    "75%": {
      transform: `translateX(0) translateY(${props.translate}px) rotate(-270deg) scale(.5)`
    },
    to: {
      transform: `rotate(-1turn)`
    }
  }))(keyframesProps)
}));

at your component you would define your classes like:

  const styleProps = {
    duration: duration,
    size: size,
    color: color
  }

  const framesProps = {
    translate: size * (1 - 0.3125)
  }
  const classes = useStyles(framesProps)(styleProps);

keyframes-hack

Upvotes: 2

Related Questions