skip to Main Content

I’ve created a hover effect for containers via css animations. It’s not so much about the color change, as the final solution is to switch images instead of divs.

The loop works, but the timings don’t work as expected in some cases.

The delay from last to first element is a bit longer, from first to second element is too short, after the loop has run through once. The more childs the container has, the more confusing the loop gets. In the 4th container, the first one doesn’t show up again.

Any ideas or even better solutions are very welcomed.

body {
  display: flex;
  gap: 10px;
  --collection-timing: 0.5s;
}

body>div {
  width: 200px;
  height: 200px;
  position: relative;
  overflow: hidden;
  cursor: pointer;
}

div div {
  position: absolute;
  width: 200px;
  height: 200px;
  animation-name: slideshow;
  animation-play-state: paused;
  animation-duration: 0.2s;
  animation-iteration-count: infinite;
  z-index: -1;
}

div div:nth-child(1) {
  animation-delay: var(--collection-timing);
  z-index: 2 !important;
}

div div:nth-child(2) {
  animation-delay: calc(var(--collection-timing) * 2);
}

div div:nth-child(3) {
  animation-delay: calc(var(--collection-timing) * 3);
}

div div:nth-child(4) {
  animation-delay: calc(var(--collection-timing) * 4);
}

div div:nth-child(5) {
  animation-delay: calc(var(--collection-timing) * 5);
}

div div:nth-child(6) {
  animation-delay: calc(var(--collection-timing) * 6);
}

div div:nth-child(7) {
  animation-delay: calc(var(--collection-timing) * 7);
}

div div:nth-child(8) {
  animation-delay: calc(var(--collection-timing) * 8);
}

div div:nth-child(9) {
  animation-delay: calc(var(--collection-timing) * 9);
}

div:has(div:nth-child(1)) div {
  animation-duration: var(--collection-timing);
}

div:has(div:nth-child(2)) div {
  animation-duration: calc(var(--collection-timing) * 2);
}

div:has(div:nth-child(3)) div {
  animation-duration: calc(var(--collection-timing) * 3);
}

div:has(div:nth-child(4)) div {
  animation-duration: calc(var(--collection-timing) * 4);
}

div:has(div:nth-child(5)) div {
  animation-duration: calc(var(--collection-timing) * 5);
}

div:has(div:nth-child(6)) div {
  animation-duration: calc(var(--collection-timing) * 6);
}

div:has(div:nth-child(7)) div {
  animation-duration: calc(var(--collection-timing) * 7);
}

div:has(div:nth-child(8)) div {
  animation-duration: calc(var(--collection-timing) * 8);
}

div:has(div:nth-child(9)) div {
  animation-duration: calc(var(--collection-timing) * 9);
}
div div:nth-child(1) {
  background: red;
}
div div:nth-child(2) {
  background: green;
}

div div:nth-child(3) {
  background: blue;
}

div div:nth-child(4) {
  background: yellow;
}

div div:nth-child(5) {
  background: greenyellow;
}

div div:nth-child(6) {
  background: orange;
}

div div:nth-child(7) {
  background: grey;
}

div div:nth-child(8) {
  background: plum;
}

body>div:hover div {
  animation-play-state: running;
}

body>div:not(:hover) div {
  z-index: -1 !important;
}

body>div:not(:hover) div:first-child {
  z-index: 2 !important;
}

@keyframes slideshow {
  0% {
    z-index: 2;
  }
  100% {
    z-index: 1;
  }
}
<div>
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
</div>


<div>
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
</div>

<div>
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>

<div>
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
</div>

2

Answers


  1. You can achieve same effect with @keyframes as mentioned by @NINE. Just need to add different background images at different percentage.

    I have created example with two images but you can use more images. Below is working example.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style>
            body {
                margin: 0;
                padding: 0;
            }
    
            .mainDiv {
                color: white;
                width: 100%;
                height: 100vh;
                background-color: #1e1e1e;
                display: flex;
                align-items: center;
                justify-content: center;
            }
    
            .sampleImg {
                width: 500px;
                height: 500px;
                background-image: url("https://images.pexels.com/photos/16534745/pexels-photo-16534745/free-photo-of-pavilions-on-gadisar-lake.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1");
                background-size: 100%;
                background-size: cover;
                background-repeat: no-repeat;
                background-position: center center;
                animation: 3s changeImg infinite;
            }
    
            @keyframes changeImg {
                0% {
                    background-image: url("https://images.pexels.com/photos/16534745/pexels-photo-16534745/free-photo-of-pavilions-on-gadisar-lake.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1");
                }
    
                50% {
                    background-image: url("https://images.pexels.com/photos/20074914/pexels-photo-20074914/free-photo-of-forest.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1");
                }
    
                100% {
                    background-image: url("https://images.pexels.com/photos/16534745/pexels-photo-16534745/free-photo-of-pavilions-on-gadisar-lake.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1");
    
                }
            }
        </style>
    </head>
    
    <body>
    
        <div class="mainDiv">
            <div class="sampleImg"></div>
        </div>
    
    </body>
    
    </html>
    Login or Signup to reply.
  2. I realise the OP requires a CSS solution, but that can get very unweildy if it’s to cover a lot of cases (in terms of number of images) so I put this JS here in case of use to other readers.

    The trick here is to use JS prepend on the last element in the list – takes it off the end and puts it at the beginning (or you could do the reverse using append if you want a different order).

    This means there is no need to do any calculation on number of images or on timing related to that. Each image simply stays at the top of the pile (ie is the one shown) for whatever time is required without needing to calculate timings.

    let interval = '';
    const parent = document.querySelector('body>div');
    parent.addEventListener('mouseover', function() {
      interval = setInterval(function() {
        parent.prepend(parent.lastElementChild);
      }, 2000);
    });
    parent.addEventListener('mouseout', function() {
      clearInterval(interval);
    });
    body>div {
      width: 200px;
      height: 200px;
      position: relative;
      overflow: hidden;
      cursor: pointer;
    }
    
    body>div>div {
      position: absolute;
      width: 200px;
      height: 200px;
    }
    <body>
      <h2>FOUR IMAGES</h2>
      <div>
        <div><img src="https://picsum.photos/id/1015/200/200"></div>
        <div><img src="https://picsum.photos/id/1016/200/200"></div>
        <div><img src="https://picsum.photos/id/1018/200/200"></div>
        <div><img src="https://picsum.photos/id/1020/200/200"></div>
      </div>
    </body>
    let interval = '';
    const parent = document.querySelector('body>div');
    parent.addEventListener('mouseover', function() {
      interval = setInterval(function() {
        parent.prepend(parent.lastElementChild);
      }, 2000);
    });
    parent.addEventListener('mouseout', function() {
      clearInterval(interval);
    });
    body>div {
      width: 200px;
      height: 200px;
      position: relative;
      overflow: hidden;
      cursor: pointer;
    }
    
    body>div>div {
      position: absolute;
      width: 200px;
      height: 200px;
    }
    <h1>SEVEN IMAGES</h1>
    <div>
      <div><img src="https://picsum.photos/id/1015/200/200"></div>
      <div><img src="https://picsum.photos/id/1016/200/200"></div>
      <div><img src="https://picsum.photos/id/1018/200/200"></div>
      <div><img src="https://picsum.photos/id/1020/200/200"></div>
      <div><img src="https://picsum.photos/id/1021/200/200"></div>
      <div><img src="https://picsum.photos/id/1022/200/200"></div>
      <div><img src="https://picsum.photos/id/1023/200/200"></div>
    </div>
    
    </body>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search