JS/JQuery Animation for Caption outside Bootstrap Carousel

Hi
I’m new here and a noobie in JS/JQuery.
I’'m building a personal Website (live example here) with the latest Bootstrap framework.
I have a Bootstrap-Carousel Component for showcasing Works.
I didn’t want to have the captions displaying over the images, i wanted them just below the Carousel.
Setting the caption position to relative and positioning the caption div outside below images results in changing the position of the Controls Arrows too.
I found then a snippet for taking the Caption-Text out from the Carousel and putting it in a div outside the Carousel.
This works, but there is no more Carousel-Images related animation.
What I’m asking for now, is if there is a way to have any kind of animation for the new Caption based on this snippet?

This is my html:

<div id="carouselActWrx" class="carousel slide">

<ol class="carousel-indicators my-auto">
    <li data-target="#carouselActWrx" data-slide-to="0" class="active"></li>
    <li data-target="#carouselActWrx" data-slide-to="1"></li>
    <li data-target="#carouselActWrx" data-slide-to="2"></li>
    <li data-target="#carouselActWrx" data-slide-to="3"></li>
  </ol>
  
<div class="carousel-inner">

<div class="carousel-item active">
  <img src="assets/images/G1S.jpg" class="d-block w-100" alt="...">
 		
        <div class="container">
		<div class="carousel-caption">
        <h5>VASE</h5>
        <p>Steinzeug, bei 1300°C red. gebrannt, danach Rauchbrand in Kapsel bei 1000°C</p>
        </div>
       </div>

</div><!--item -->

<div class="carousel-item">
  <img src="assets/images/G2S.jpg" class="d-block w-100" alt="...">
 		
        <div class="container">
 		<div class="carousel-caption">
        <h5>KUMME</h5>
        <p>Steinzeug, bei 1300°C red. gebrannt, danach Rauchbrand in Kapsel bei 1000°C</p>
        </div>
		</div>

</div><!--item -->

<div class="carousel-item">
  <img src="assets/images/G3S.jpg" class="d-block w-100" alt="...">
 		
        <div class="container">
		<div class="carousel-caption">
        <h5>VASE</h5>
        <p>Steinzeug, bei 1300°C red. gebrannt, danach Rauchbrand in Kapsel bei 1000°C</p>
      	</div>
		</div>

</div><!--item -->

<div class="carousel-item">
  <img src="assets/images/G4S.jpg" class="d-block w-100" alt="...">
 		
        <div class="container">
		<div class="carousel-caption">
        <h5>GEF&Auml;SS</h5>
        <p>Steinzeugton, bei 1100°C oxid. gebrannt, danach Rauchbrand in Kapsel bei 1000°C</p>  
      	</div>
		</div>

</div><!--item -->

</div><!--End Inner -->

<a class="carousel-control-prev" href="#carouselActWrx" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>


<a class="carousel-control-next" href="#carouselActWrx" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>

</div><!--End carouselActWrx -->

<!--New html caption -->
 <div id="capt_carousel">
        <div class="capt-below">
        <span></span>
        </div>
    </div>

this the css:

.capt-below {
	 padding-top: 20px;	
	 font-family: 'Open Sans', sans-serif; 
     text-align: center;
	 color: #7c8089;     
    }
    .capt-below h5 {
     font-weight: 400;
	 font-size: 14px;
    }
    .capt-below p {
	 font-weight: 300;
	 font-size: 12px;
     }
    
	 #capt_carousel{
	 margin-top: 15px;	 
	 border-top: 2px solid #7c8089;
     }

and this the script:

$(function() {
		$('#carouselActWrx').carousel();
	
		var caption = $('div.carousel-item:nth-child(1) .carousel-caption');
		$('#capt_carousel span').html(caption.html());
		caption.css('display','none');
			
		$('#carouselActWrx').on('slide.bs.carousel', function(evt) {
    	var caption = $('div.carousel-item:nth-child(' + ($(evt.relatedTarget).index()+1) + ') .carousel-caption');
    	$('#capt_carousel span').html(caption.html());
    	caption.css('display','none');

		});	

		});

Would be nice if someone would help finding a solution…
Thanks in advance.

For a css solution only in the default bootstrap carousel set up you could have just changed the bottom position of the caption assuming you don’t have loads of content in there (which seems to be true from your demo).

.carousel-inner {padding-bottom:100px;}
.carousel-caption {bottom:-100px}

Which would have looked like this on the default carousel.

If you have caption content that wraps or needs to take up the flow then you will need to do what you have already done. You could animate it with CSS keyframes but it would require a class being added and removed from the current new caption item.

That would require the help of a js guru (which I am not ):slight_smile:

I had a fiddle anyway and got the js to add a class that we could animate with CSS.

I added this extra CSS.

/*.......................*/
#capt_carousel {
  overflow: hidden;
}

.capt-below span.activeLeft {
  opacity: 0;
  transform: translateX(100%);
  animation: captionLeft 0.5s forwards;
  display: block;
}
.capt-below span.activeRight {
  opacity: 0;
  transform: translateX(-100%);
  animation: captionRight 0.5s forwards;
  display: block;
}

@keyframes captionLeft {
  from {
    opacity: 0;
    transform: translateX(100%);
  }

  to {
    opacity: 1;
    transform: translateX(0);
  }
}
@keyframes captionRight {
  from {
    opacity: 0;
    transform: translateX(-100%);
  }

  to {
    opacity: 1;
    transform: translateX(0);
  }
}

Then I changed your js to this:

$(function () {
  $("#carouselActWrx").carousel();

  var caption = $("div.carousel-item:nth-child(1) .carousel-caption");
  $("#capt_carousel span").html(caption.html());
  caption.css("display", "none");

  $("#carouselActWrx").on("slide.bs.carousel", function (evt) {
    var caption = $(
      "div.carousel-item:nth-child(" +
        ($(evt.relatedTarget).index() + 1) +
        ") .carousel-caption"
    );
    $("#capt_carousel span").html(caption.html());

    $("#capt_carousel span").removeClass("activeLeft activeRight");
    caption.css("display", "none");
    $("#capt_carousel span")[0].offsetWidth; //trigger reflow to start keyframe
    if (evt.direction === "left") {
      $("#capt_carousel span").addClass("activeLeft");
    }
    if (evt.direction === "right") {
      $("#capt_carousel span").addClass("activeRight");
    }
  });
});

It’s probably the wrong way to do it but may serve as a starting point until the experts here arrive :slight_smile:

1 Like

Hi Paul
Many thanks for taking so much of your time for helping me out.
Your first css-only suggestion goes in the same direction as some others I had already tried.
They all have one thing in common, the controls arrows are also repositioned and then you have to frickle around to find the right position at different breakpoints because I wanna have the arrows always vertically centered.
But your second suggestion, that’s the one!
This is really an excellent starting point to play around with. Based on your code maybe I can try to add also a slight fadeIn/FadeOut transition or delay or whatever before the sliding (which seams to be really in time with images) takes place.
So in absence of a big expert guru I choose you as my guru. Let’s start playing…

I really appreciate your help, many thanks again.

There is already an opacity effect and you can just mess around with the keyframe to make it happen differently. If you put in a 50% keyframe you can finish the slide before the fade in finishes. The timing of the animation can be elongated to cater for the slide finishing at 50%.

e.g.

/*.......................*/
#capt_carousel {
  overflow: hidden;
}

.capt-below span.activeLeft {
  opacity: 0;
  transform: translateX(100%);
  animation: captionLeft 1s forwards;
  display: block;
}
.capt-below span.activeRight {
  opacity: 0;
  transform: translateX(-100%);
  animation: captionRight 1s forwards;
  display: block;
}

@keyframes captionLeft {
  0% {
    opacity: 0;
    transform: translateX(100%);
  }

  50% {
    opacity: 0.4;
    transform: translateX(0);
  }

  100% {
    opacity: 1;
    transform: translateX(0);
  }
}
@keyframes captionRight {
  0% {
    opacity: 0;
    transform: translateX(-100%);
  }

  50% {
    opacity: 0.4;
    transform: translateX(0);
  }

  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

I updated the previous codepen with that example in place so you can see it in action.

It’s just a matter of adding in extra keyframes at whatever percentage you need and then adjusting the timing duration to cater for the effects you want. In my example I finish the slide in at 50% and therefore for the rest of the duration the element keeps fading by itself. Originally the slide in and fade finished at the same time so the effectwasn’t as pronounced.

1 Like

Thanks a lot for the improvements.
Now I’m wondering if there is way to let slide out and fade out the old caption text before the new caption slides in with css:

/*.......................*/

.caption_fadeOut {
	animation-name: fadeOut;
	-webkit-animation-name: fadeOut;
	-webkit-animation-duration: 3s;
	-webkit-animation-timing-function: ease;
	animation-fill-mode: forwards;
	animation-delay: 250ms;
}


@keyframes fadeOut {
	0% {
		opacity: 1;
	}
	
	20% {
		opacity: 0.8;
	}
	
	50% {
		opacity: 0.4;
	}
	
	100% {
		opacity: 0;
	}
}

@-webkit-keyframes fadeOut {
	0% {
		opacity: 1
	}
	
	20% {
		opacity: 0.8;
	}
	
	50% {
		opacity: 0.4;
	}
	
	100% {
		opacity: 0
	}
}

That’s not strictly possible as the JS you are using is inserting only one caption when the button is clicked. There’s only one caption element in that section unlike the normal bootstrap caption where you have a series of caption elements.

The js is just copying one element at a time so the html for the new caption is just the one caption. You can’t slide one caption out and then slide in a new caption because you don’t keep a copy of the old caption.

However having said all that you can probably trick people into thinking that the slideout and in effect is happening to different captions if you do it quick enough and of course do it while the js is changing the html.

This requires changing the keyframes to first slide the element out to one side and then immediately sliding it back from the other direction.

Here’s the example:

This was the new CSS:

/*.......................*/
#capt_carousel {
  overflow: hidden;
}

.capt-below span.activeLeft {
  opacity: 0;
  transform: translateX(100%);
  animation: captionLeft 0.7s forwards;
  display: block;
}
.capt-below span.activeRight {
  opacity: 0;
  transform: translateX(-100%);
  animation: captionRight 0.7s forwards;
  display: block;
}

@keyframes captionLeft {
  0% {
    opacity: 1;
    transform: translateX(0%);
  }

  49.999% {
    opacity: 1;
    transform: translateX(-100%);
  }

  50% {
    opacity: 0;
    transform: translateX(100%);
  }

  100% {
    opacity: 1;
    transform: translateX(0);
  }
}
@keyframes captionRight {
  0% {
    opacity: 1;
    transform: translateX(0%);
  }

  49.999% {
    opacity: 1;
    transform: translateX(100%);
  }

  50% {
    opacity: 0;
    transform: translateX(-100%);
  }

  100% {
    opacity: 1;
    transform: translateX(0);
  }
}
1 Like

:astonished: :star_struck: magic!

Didn’t think about there is only one caption.
I think that’s nice enough now. Everything works. The emulation for the Carousels default slideIn/slideOut is there and I can keep control arrows in place.

Many thanks for helping and explanations. This helped a lot understanding whats going on, and maybe, who knows, one day I will also be able to write some code…

Cheers, have a nice sunday!

1 Like