How to apply a CSS shimmer animation to a collection of HTML elements together
Asked Answered
A

2

6

I would like to apply a shimmer animation to a collection of HTML elements but have it be in unison as if it is one shimmer running through all the elements. I have seen this done with animations within Text by using background-clip: text; in the CSS and applying the animation to parent element.

Is it possible to do this kind of animation to a collection of elements in unison similar to using background-clip:text, but instead clipping to the HTML elements instead of text?

Link to fiddle

.shimmer-box {
  width: 300px;
  height: 75px;
  margin: 20px 20px;
}

.animation {
  background: linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%);
  background-size: 300%;
  animation-name: shimmer;
  animation-duration: 1000ms;
  animation-timing-function: linear;
  animation-delay: 0;
  animation-iteration-count: infinite;
  animation-direction: normal;
  animation-fill-mode: none;
  animation-play-state: running;  
}

@keyframes shimmer {
  0% {
    background-position-x: 100%;
  }

  100% {
    background-position-x: 0%;
  }
}

.gray {
  background-color: #e2e2e2;
}

.flex-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.circle {
  width: 30px;
  height: 30px;
  border-radius: 50%;
}

.short-row {
  height: 30px;
  flex: 1;
  border-radius: 8px;
}

.body-box {
  margin-top: 12px;
  width: 342px;
  height: 100px;
  border-radius: 8px;
}

.container {
  display: inline-block;
}
<h4>This animation works on a single element</h4>
<div class="shimmer-box animation"></div>

<h4>If I apply the animation to each element they are disjointed</h4>
<div class="container">
  <div class="flex-row">
    <div class="circle gray animation"></div>
    <div class="short-row gray animation"></div>
    <div class="short-row gray animation"></div>
  </div>
  <div class="body-box gray animation"></div>
</div>

<div>
<h4>I would like this animation to run in unison on all the elements here</h4>
<paragraph>Ideally it looks like one shimmer running accross all the elements in unison</paragraph>
</div>
<div class="container">
  <div class="flex-row">
    <div class="circle gray"></div>
    <div class="short-row gray"></div>
  </div>
  <div class="body-box gray"></div>
</div>
Alexi answered 16/8 at 17:47 Comment(0)
L
5

Add the following line to the .animation ruleset:

background-attachment: fixed;

This attaches the background of all elements of that class to the viewport in such a way that they appear synchronised.

.shimmer-box {
  width: 300px;
  height: 75px;
  margin: 20px 20px;
}

.animation {
  background: linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%);
  /* add the following line: */
  background-attachment: fixed;
  background-size: 300%;
  animation-name: shimmer;
  animation-duration: 1000ms;
  animation-timing-function: linear;
  animation-delay: 0;
  animation-iteration-count: infinite;
  animation-direction: normal;
  animation-fill-mode: none;
  animation-play-state: running;
}

@keyframes shimmer {
  0% {
    background-position-x: 100%;
  }
  100% {
    background-position-x: 0%;
  }
}

.gray {
  background-color: #e2e2e2;
}

.flex-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.circle {
  width: 30px;
  height: 30px;
  border-radius: 50%;
}

.short-row {
  height: 30px;
  flex: 1;
  border-radius: 8px;
}

.body-box {
  margin-top: 12px;
  width: 342px;
  height: 100px;
  border-radius: 8px;
}

.container {
  display: inline-block;
}
<h4>This animation works on a single element</h4>
<div class="shimmer-box animation"></div>

<h4>If I apply the animation to each element they are disjointed</h4>
<div class="container">
  <div class="flex-row">
    <div class="circle gray animation"></div>
    <div class="short-row gray animation"></div>
    <div class="short-row gray animation"></div>
  </div>
  <div class="body-box gray animation"></div>
</div>

<div>
  <h4>I would like this animation to run in unison on all the elements here</h4>
  <paragraph>Ideally it looks like one shimmer running accross all the elements in unison</paragraph>
</div>
<div class="container">
  <div class="flex-row">
    <div class="circle gray"></div>
    <div class="short-row gray"></div>
  </div>
  <div class="body-box gray"></div>
</div>

JS Fiddle demo.

References:

Lustick answered 16/8 at 18:24 Comment(4)
There are some odd behaviours, properties, and values in CSS; I read about this one years ago in an article by Eric Meyer (meyerweb.com/eric/css/edge/complexspiral/demo.html). It was so good it stuck with me.Lustick
Ah yes, MeyerWeb - I was reading his blog when I started high-school, lol (c.f. MezzoBlue, SeanInman, friendsOfED, Phong and more... nostalgia...). Crazy that this effect is now 23 years old: 2001.Faydra
Absolutely, I was genuinely amazed when I read that it was a feature of CSS1; how far ahead of the curve they were at that early stage blows my mind.Lustick
Speaking of being ahead-of-the-curve... Acko's website has been putting everyone else to shame for... probably 15 years now?Faydra
I
3

Here is an idea where you can control your animation using two classes. A wrapper class to be used on the container where all the elements should animate the same. And the animation class to be added to the element that should animate. Both can be used on the same element if only one element is concerned.

.wrapper {
  position: relative; 
  z-index: 0;
}
.animation {
  mask: linear-gradient(#000 0 0); /* clip the pseudo-element to the element boundary (no, overflow: hidden won't work) */
}
/*
  the pseudo element is positioned relatively to the wrapper 
  making all the animations synchronized 
*/
.animation:before {
  content:"";
  position: absolute;
  z-index: -1;
  inset: 0;
  background: 
    linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%)
    right/300% 100%;
  animation: shimmer 1s linear infinite;
}
@keyframes shimmer {
  to { background-position: left;}
}


/* irrelevant code*/
.gray {
  background-color: #e2e2e2;
}
.shimmer-box {
  width: 300px;
  height: 75px;
  margin: 20px 20px;
}
.flex-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
.circle {
  width: 30px;
  height: 30px;
  border-radius: 50%;
}
.short-row {
  height: 30px;
  flex: 1;
  border-radius: 8px;
}
.body-box {
  margin-top: 12px;
  width: 342px;
  height: 100px;
  border-radius: 8px;
}
.container {
  display: inline-block;
}
<div class="wrapper shimmer-box animation"></div>
<hr>
<div class="container wrapper">
  <div class="flex-row">
    <div class="circle gray animation"></div>
    <div class="short-row gray animation"></div>
    <div class="short-row gray animation"></div>
  </div>
  <div class="body-box gray animation"></div>
</div>
<hr>
<div class="container wrapper" style="margin-left: 200px">
  <div class="flex-row">
    <div class="circle gray animation"></div>
    <div class="short-row gray animation"></div>
    <div class="short-row gray animation"></div>
  </div>
  <div class="body-box gray animation"></div>
  <div class="body-box gray animation"></div>
</div>
Incandescence answered 16/8 at 19:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.