Search code examples
cssreactjscss-animationsreact-componentkeyframe

Rotate image from random start location in React using Keyframes


Using React, I have a component that I want to start rotated from a random location and rotate 360 degrees infinitely. What I have is:

HTML:

return <img className={`${props.className}`} style={{
        animation: `spin 5s linear infinite`,
        transform: `rotate(${Math.floor(Math.random() * (361));}deg)`
    }} src={image} alt="img"/>

CSS:

@keyframes spin {
    from {transform:rotate(0deg);}
    to {transform:rotate(360deg);}
}

By default, what this does is ignore the transform, load the image so that it is upright (i.e. 0 degrees), and rotate to 360 degrees (vertical again) over the course of 5 seconds. What I want is a way for from to be transform:rotate(deg);} and to to be {transform:rotate(<SOME RANDOM NUMBER + 360>deg);}. Is this possible?

As a side note, is it possible to randomize the direction it travels?


Solution

  • You can't get the required effect using transform rotate - as you have noted the animation will immediately alter this to rotate(0).

    But what you can do is leverage the animation-delay CSS property.

    Instead of calculating a random rotation, calculate a random time between 0 and 5 seconds and use that, negative, to set animation-delay.The animation will start part way through its cycle, at a random point and so at a random angle.

    Here's a pure CSS/HTML/JS snippet with the rotation slowed down so it's easier to see that the rotation does start at a different angle each time:

    const spinner = document.querySelector('.spin');
    spinner.style.setProperty('--delay', Math.floor(Math.random() * 50));
    .spin {
      width: 100px;
      height: 100px;
      background: red;
      animation: spin 50s linear infinite;
      animation-delay: calc(var(--delay) * -1s);
    }
    
    @keyframes spin {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }
    <div class="spin"></div>