4

CODE

I have 3 divs with text (1, 2, 3).

When the user hovers over each div, it should change the image.

I have a 1s ease transition to make it smooth.

BUG

If you hover over different divs too quickly, the transition is choppy or doesn't load. I'm looking for a solution where the user can quickly move through the divs and the image will transition slowly to whichever is the newest div. Thank you in advance.

CODEPEN DEMO

Codepen

LIVE DEMO:

img1 = 'url(https://images.unsplash.com/photo-1533273859801-d731381dfe2d)';
img2 = 'url(https://images.unsplash.com/photo-1534939444268-6a9ff2733c32)';
img3 = 'url(https://images.unsplash.com/photo-1534841515798-3d43f5434123)';

$('.p1').hover(function(){
	$('.bg').css({'background-image': img1});
});

$('.p2').hover(function(){
	$('.bg').css({'background-image': img2});
});

$('.p3').hover(function(){
	$('.bg').css({'background-image': img3});
});
body {
  margin: 0;
  font-family: sans-serif;
}

.bg {
  position: absolute;
  width: 100%;
  height: 100%;
  background: url(https://images.unsplash.com/photo-1533273859801-d731381dfe2d) no-repeat center;
  background-size: cover;
  z-index: -1;
  background-color: #989898;
  background-blend-mode: multiply;
  transition: background-image 1s ease
}

.projects {
  position: absolute;
  margin: auto;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100vw;
  height: 16em;
}

.p {
  font-size: 1.2em;
  position: relative;
  padding: 2rem 0;
  color: #fff;
  opacity: 0.5;
  letter-spacing: 4px;
  text-indent: 4px;
  font-weight: 400;
  transition: opacity 0.5s ease;
  text-align: center;
}

.p:hover {
  opacity: 1;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="bg"></div>
<div class="projects">
  <div class="p p1">1</div>
  <div class="p p2">2</div>
  <div class="p p3">3</div>
</div>

11
  • How big are those images? Never mind choppy transition, they almost froze my computer. Commented Aug 23, 2018 at 22:00
  • @ibrahimmahrir haha, they're from unsplash, they're high res but with my fast wifi it's still a problem. The transition bug is independent. Commented Aug 23, 2018 at 22:03
  • Does the bug persist after a few hover-overs/hover-aways? Commented Aug 23, 2018 at 22:19
  • Yes, still buggy Commented Aug 23, 2018 at 22:24
  • @ibrahimmahrir updated the codepen demo with lower res images codepen.io/grysn/pen/jvbBQd?editors=0110 still buggy Commented Aug 23, 2018 at 22:27

2 Answers 2

2

I suggest you to use the mousemove event and the .on() method instead of the .hover() method.

That, to be able to "buffer" events... Without missing one.

.hover() method is the same as defining a mouseenter and mouseout event handler (the second being optionnal).

With the mousemove event, the chances for the event to fire correctly (from a user point of view) are higher since it fires like a machinegun.

Why... Because you will have to "buffer" those events to wait for the current transition to end.

Now, you will attach the event handler to the common class .p and set a timeout of 600ms of "mouse inactivity". After that delay, the background wil be updated with the image corresponding with the last .p mousemoved.

While the user just move the mouse like someone with parkinson's desease, nothing happens about the background. It is updated and animated only on mousemove stop.

img1 = 'url(http://ejournalz.com/wp-content/uploads/2017/06/Dog-Care.jpg)';
img2 = 'url(https://www.focusdogtraining.com/wp-content/uploads/2017/10/puppies.jpg)';
img3 = 'url(https://www-tc.pbs.org/wgbh/nova/assets/img/full-size/clever-dog-lab-merl.jpg)';

var movement;
$(".p").on("mousemove",function(e){

  var target = $(e.target);

  clearTimeout(movement);
  movement = setTimeout(function(){
    console.log("User stopped moving... Updating the image.")

    if(target.hasClass("p1")){
      console.log("image #1");
      $('.bg').css({'background-image': img1});
    }
    if(target.hasClass("p2")){
      console.log("image #2");
      $('.bg').css({'background-image': img2});
    }
    if(target.hasClass("p3")){
      console.log("image #3");
      $('.bg').css({'background-image': img3});
    }
  },600);
});

A working demo is best viewed on CodePen than in a SO snippet (sadly).

Sign up to request clarification or add additional context in comments.

2 Comments

Salut Mec. This works pretty well, but I noticed that if you move quickly through the divs, it can get stuck on the wrong image. I've been trying with .fadeIn() and fadeOut() but no luck so far.
Salut à toi! I found a better way to buffer the mousemove events, which should be perfect now. Have a look at the CodePen.
0

Ok. This is impossible using CSS, so here is a how to do it using jQuery.

To acheive the transition, we'll have to use two divs, one in the background and one in the front that will fade in giving us the illusion of transition. The .bg div should contain those two divs:

<div class="bg">
  <div class="back"></div>
  <div class="front"></div>
</div>

The css should change to:

.bg {
  position: absolute;
  width: 100%;
  height: 100%;
  z-index: -1;
  background-color: #989898;
  background-blend-mode: multiply;
  transition: background-image 1s ease
}

.bg > * {
  background: url(http://ejournalz.com/wp-content/uploads/2017/06/Dog-Care.jpg) no-repeat center;
  background-size: cover;
  position: absolute;
  width: 100%;
  height: 100%;
}

Which just removes any css transitions and make the inner divs overlap and as big as their parent .bg.

Now for the js part - where the actual transition takes place. When the mouse enters a .p area, we just call the function changeTo with the corresponding image. The function takes the current image from the front to the back, sets the new image as the front image. Doing that will be choppier than the css transition because the opacity doesn't match. Making the front image the back one should be accompaigned with an invertion of the opacity of the front. That is achieved by just getting the opacity and substracting it from 1. After we do that, we just animate the front image opacity from whatever value it is now to 1. The speed of this animation should be relative to what the opacity is:

var $back = $(".bg .back"),
  $front = $(".bg .front");

function changeTo(img) {
  $front.stop();                                                            // stop any animation that may be in progress
  var op = $front.css("opacity");                                           // get the opacity value (stoping the animation will let the opacity hanging between 0 and 1 inclusive, so we get that value to transition from it)
  $back.css("background-image", $front.css("background-image"));            // make the front image the back one
  $front.css({
    opacity: 1 - op,                                                        // because we made the front image the back one we need to invert the front image's opacity to give the illusion that the back image (which is now the front one) is shown at the original opacity. That's how math works!
    "background-image": img                                                 // and of course make the front image our desired one
  });
  $front.animate({                                                          // animate the front image's
    opacity: 1                                                              // ... opacity from whatever value it is to 1
  }, 500 * op, "linear");                                                   // in a period of 0.5s accouunting for the opacity delta, in other words animating from opacity 0.5 to 1 will take only half the time as animating from 0 to 1. That also how math works! :P
}

$('.p1').on("mouseenter", function() {                                       // when mouse enters .p1
  changeTo(img1);                                                            // change to img1
});

$('.p2').o...

Working example:

var img1 = 'url(http://ejournalz.com/wp-content/uploads/2017/06/Dog-Care.jpg)';
var img2 = 'url(https://www.focusdogtraining.com/wp-content/uploads/2017/10/puppies.jpg)';
var img3 = 'url(https://www-tc.pbs.org/wgbh/nova/assets/img/full-size/clever-dog-lab-merl.jpg)';

var $back = $(".bg .back"),
  $front = $(".bg .front");

function changeTo(img) {
  $front.stop();
  var op = $front.css("opacity");
  $back.css("background-image", $front.css("background-image"));
  $front.css({
    opacity: 1 - op,
    "background-image": img
  });
  $front.animate({
    opacity: 1
  }, 500, "linear");
}

$('.p1').on("mouseenter", function() {
  changeTo(img1);
});

$('.p2').on("mouseenter", function() {
  changeTo(img2);
});

$('.p3').on("mouseenter", function() {
  changeTo(img3);
});
.bg {
  position: absolute;
  width: 100%;
  height: 100%;
  z-index: -1;
  background-color: #989898;
  background-blend-mode: multiply;
  transition: background-image 1s ease
}

.bg > * {
  background: url(http://ejournalz.com/wp-content/uploads/2017/06/Dog-Care.jpg) no-repeat center;
  background-size: cover;
  position: absolute;
  width: 100%;
  height: 100%;
}

.projects {
  position: absolute;
  margin: auto;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100vw;
  height: 16em;
}

.p {
  font-size: 1.2em;
  position: relative;
  padding: 2rem 0;
  color: #fff;
  opacity: 0.5;
  letter-spacing: 4px;
  text-indent: 4px;
  font-weight: 400;
  transition: opacity 0.5s ease;
  text-align: center;
}

.p:hover {
  opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="bg">
  <div class="back"></div>
  <div class="front"></div>
</div>
<div class="projects">
  <div class="p p1">1</div>
  <div class="p p2">2</div>
  <div class="p p3">3</div>
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.