Random “start point” for CSS keyframes animation

2019-04-07 06:14发布

问题:

I have a list of boxes with a background image that scrolls vertically with:

@keyframes movie {
   0% { background-position: 50% 5%; }
   50% { background-position: 50% 95%; }
   0% { background-position: 50% 5%; }
}

.movie {
    animation: movie 50s linear infinite;
}

The "problem" is that in this way all the boxes have the background moving at the same time.
I'd like to have a "random start point" so that each box has a different animation.

For example, one background is moving down while another is moving up.

It is possible with pure CSS? I can't find a simple way neither with Javascript..

回答1:

You can use negative animation delay.

https://developer.mozilla.org/en-US/docs/Web/CSS/animation-delay

Specifying a negative value for the animation delay causes the animation to begin executing immediately. However, it will appear to have begun executing partway through its cycle. For example, if you specify -1s as the animation delay time, the animation will begin immediately but will start 1 second into the animation sequence.

So if you want your animation start at 20%, animation delay would be ( -50s * 20% ). You just need to use javascript to create random start point.



回答2:

You can use animation-delay.

animation-delay: 10s;

Or inside your shorthand:

animation: movie 50s linear 10s infinite;

Maybe easier to handle, with some pseudo-classes:

.movie:nth-of-type(1) {
  animation-delay: 10s;
}

.movie:nth-of-type(2) {
  animation-delay: 20s;
}

.movie:nth-of-type(3) {
  animation-delay: 30s;
}


回答3:

This can be done with pure CSS, without writing (or generating via SCSS etc), using a combination of:

  • A negative animation-delay to change the start time of the animation
  • Multiple nth-child or nth-of-type rules to apply formulas that will 'randomize' rule application
movie.nth-child(2n) { animation-delay: -10s }  
movie.nth-child(2n+1) { animation-delay: -30s }  
movie.nth-child(3n) { animation-delay: -20s; }  
movie.nth-child(5n) { animation-delay: -40s }  
movie.nth-child(7n) { animation-delay: -15s }  
{etc}

Using just the first 2 rules gives alternating rules (e.g. even/odd rows in a table). Notice the second rule which has a +1 offset - this is important if your class (movie) doesn't have an appropriate default for the rule you are changing (0 by default anyways for animation-delay).

Using nth-child(n) formulas with prime multiples of n makes an effective pattern length equal to the product of all your prime factors (e.g. 2*3*5*7 = 210 elements before repeating).

li {
  animation: movie 5s linear infinite;
}
@keyframes movie {
  20% { color: white }
  40% { color: black }
}
li:nth-child(2n-1) {
  background-color: lime;
  animation-delay: 1s;
}
li:nth-child(2n) {
  background-color: orange;
  animation-delay: 2s;
}
li:nth-child(3n) {
  background-color: yellow;
  animation-delay: 3s;
}
li:nth-child(5n) {
  background-color: magenta;
  animation-delay: 5s;
}
li:nth-child(7n) {
  background-color: aqua;
}
<ul>
  <li>0</li>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
  <li>7</li>
  <li>8</li>
  <li>9</li>
  <li>10</li>
  <li>11</li>
  <li>12</li>
  <li>13</li>
</ul>

For further randomization, you could create a second set of rules with slightly different n multiples/offsets and change the animation-duration (or any other rule really).



回答4:

To elaborate on the suggestion in Chef's answer, the Javascript to randomise the animation delays on a bunch of elements might look something like this:

var elements = document.querySelectorAll('.movie')
var animationDuration = 50000; // in milliseconds

// Set the animationDelay of each element to a random value
// between 0 and animationDuration:
for (var i = 0; i < elements.length; i++) {
  var randomDuration = Math.floor(Math.random() * animationDuration);
  elements[i].style.animationDelay = randomDuration + 'ms';  
}

Of course, you can multiply randomDuration by -1 if you want to use negative values for animation delay (so some elements start mid-animation rather than having their initial animation delayed).