Home > Back-end >  Image won't stay visible for hover effect
Image won't stay visible for hover effect

Time:10-09

Hello and thank you in advance for reading my question.

GOAL: Set image so that once it's scrolled into view it transitions smoothly into a set position - but still reacts to :hover. Using @keyframes and a little JavaScript, I set the image to opacity: 0 and it's final opacity to opacity: .85. Then I added a hover effect in CSS to make it's opacity: 1

The issue is once it's finished with it's transition - it disappears - reverting to it's original opacity which is zero. I managed to make it freeze at .85 with animation-fill-mode: forwards, rather than animation-fill-mode: none, but then it won't respond to :hover

And here's a test snippet of the problem in action:

let observer_img = new IntersectionObserver(updates => {
    updates.forEach(update => {
        if (update.isIntersecting) {
            update.target.classList.add('shift_frame_center_img');
        } else {
            update.target.classList.remove('shift_frame_center_img');
        }
    });
}, { threshold: 0 });

[...document.querySelectorAll('.features-img-wrapper img')].forEach(element => observer_img.observe(element));
body {
  width: 100%;
  height: 100vh;
  background-color: lightgrey;
}

/* CHILD */
.features-img-wrapper img {
  width: 10rem;
  display: block;
  margin-left: auto;
  margin-right: auto;
  margin-top: 8rem;
  opacity: 0;
  transition: all .5s;
}

/* APPEND-CHILD */
.shift_frame_center_img {
  animation: center_img 1s 0.5s none;
}

/* CHILD ON HOVER */
.features-img-wrapper img:hover {
  opacity: 1;
  transform: scale(1.035);
}

/* KEYFRAMES */
@keyframes center_img {
  0% {
    transform: translateY(20rem);
  }
  100% {
    transform: translateY(0);
    opacity: .85;
  }
}
<body>
  <div class="features-img-wrapper">
      <img src="https://synapse.it/wp-content/uploads/2020/12/test.png">
  </div>
</body>

If I could get a hand with this that would be wonderful, I'm a bit of a beginner and have already spent a few hours on this, all feedback welcome. Thank you very much.

CodePudding user response:

Solution 1

To understand why the hover effect was not working with the animation-fill-mode: forwards, read this answer.

You can fix that by adding !important property to the hover styles:

.features-img-wrapper img:hover {
  opacity: 1 !important;
  transform: scale(1.035) !important;
}

The problem, in this case, is that the transition will not work for hover.

Solution 2

You could remove the animation entirely and add the final state styles to the shift_frame_center_img class.

But you would still need to use the !important property because of the CSS Specificity.

let observer_img = new IntersectionObserver(updates => {
    updates.forEach(update => {
        if (update.isIntersecting) {
            update.target.classList.add('shift_frame_center_img');
        } else {
            update.target.classList.remove('shift_frame_center_img');
        }
    });
}, { threshold: 0 });

[...document.querySelectorAll('.features-img-wrapper img')].forEach(element => observer_img.observe(element));
body {
  width: 100%;
  height: 100vh;
  background-color: lightgrey;
}

/* CHILD */
.features-img-wrapper img {
  width: 10rem;
  transform: translateY(20rem);
  display: block;
  margin-left: auto;
  margin-right: auto;
  margin-top: 8rem;
  opacity: 0;
  transition: all .5s;
}

/* APPEND-CHILD */
.shift_frame_center_img {
  transform: none !important;
  opacity: .85 !important;
}

/* CHILD ON HOVER */
.features-img-wrapper img:hover {
  opacity: 1 !important;
  transform: scale(1.035) !important;
}
<body>
  <div class="features-img-wrapper">
    <img src="https://synapse.it/wp-content/uploads/2020/12/test.png">
  </div>
</body>

CodePudding user response:

This snippet removes the need for fill-mode forwards by setting the img to have opacity 1 as its initial state so it will revert to that at the end of the animation.

The animation itself is altered to take 1.5s rather than 1s with the first third simply setting the img opacity to 0 so it can't be seen. This gives the delay effect.

let observer_img = new IntersectionObserver(updates => {
    updates.forEach(update => {
        if (update.isIntersecting) {
            update.target.classList.add('shift_frame_center_img');
        } else {
            update.target.classList.remove('shift_frame_center_img');
        }
    });
}, { threshold: 0 });

[...document.querySelectorAll('.features-img-wrapper img')].forEach(element => observer_img.observe(element));
body {
  width: 100%;
  height: 100vh;
  background-color: lightgrey;
}

/* CHILD */
.features-img-wrapper img {
  width: 10rem;
  display: block;
  margin-left: auto;
  margin-right: auto;
  margin-top: 8rem;
  opacity: 0;
  transition: all .5s;
    opacity: 1;
}

/* APPEND-CHILD */
.features-img-wrapper img {
  animation: center_img 1.5s 0s none;
}

/* CHILD ON HOVER */
.shift_frame_center_img:hover {
  opacity: 1;
  transform: translateY(0) scale(1.035);
}

/* KEYFRAMES */
@keyframes center_img {
  0% {
    transform: translateY(20rem) scale(1);
    opacity: 0;
  }
    33.33% {
    transform: translateY(20rem) scale(1);
    opacity: 0;
  }
  100% {
    transform: translateY(0) scale(1);
    opacity: .85;
  }
}
<body>
  <div class="features-img-wrapper">
      <img src="https://synapse.it/wp-content/uploads/2020/12/test.png">
  </div>
</body>

Note: as each transform setting will reset anything that isn't included both tranlateY and scale are included in each setting.

Outside the SO snippet system it was possible to leave the animation settings untouched by chaining another animation to the front which ran for 0.5s and just set the img to opacity: 0. This did not work in the snippet system (it got into a loop of flashing on and off) hence the introduction of one but extended animation.

  • Related