I have a full-page jssor slider. As pictures are scaled to 'cover' they're cut off on the bottom if they're too tall.
I would like to be able to specify for each image whether it should vertically centered or bottom-aligned (or the default top-aligned), so I can make sure the important parts of each image are always showing.
I've tried vertical-align
styles on the div
containing the img
with no luck.
Then I've tried calculating the required offset and applying a negative margin-top
to images that are too tall – code below. This works at full width, but pushes the pictures too high when I resize the window below a certain width – leaving blank space on the bottom. I haven't been able to figure out how to stop this from happening.
Has anyone gotten something like this to work? Is there built-in functionality for this that I've missed?
HTML (I would add the show-bottom class only on relevant slides):
<div id="slider1_container" style="position: relative; margin: 0 auto; top: 0px; left: 0px; width: 1200px; height: 900px; display: none;">
<!-- Slides Container -->
<div u="slides" style="cursor: move; position: absolute; left: 0px; top: 0px; width: 1200px; height: 900px; overflow: hidden;">
{% for slide in slides %}
<div><img class="slide-image show-bottom" u="image" src="/site_media/{{ slide }}" /></div>
{% endfor %}
</div>
</div>
JS:
var options = {
$SlideDuration: 700, //[Optional] Specifies default duration (swipe) for slide in milliseconds, default value is 500
$DragOrientation: 1, //[Optional] Orientation to drag slide, 0 no drag, 1 horizental, 2 vertical, 3 either, default value is 1 (Note that the $DragOrientation should be the same as $PlayOrientation when $DisplayPieces is greater than 1, or parking position is not 0)
$AutoPlay: true, //[Optional] Whether to auto play, to enable slideshow, this option must be set to true, default value is false
$AutoPlayInterval: 4000, //[Optional] Interval (in milliseconds) to go for next slide since the previous stopped if the slider is auto playing, default value is 3000
$SlideEasing: $JssorEasing$.$EaseInQuart,
$ArrowKeyNavigation: true, //[Optional] Allows keyboard (arrow key) navigation or not, default value is false
$PauseOnHover: 0, //[Optional] Whether to pause when mouse over if a slider is auto playing, 0 no pause, 1 pause for desktop, 2 pause for touch device, 3 pause for desktop and touch device, 4 freeze for desktop, 8 freeze for touch device, 12 freeze for desktop and touch device, default value is 1
$FillMode: 2, //[Optional] The way to fill image in slide, 0 stretch, 1 contain (keep aspect ratio and put all inside slide), 2 cover (keep aspect ratio and cover whole slide), 4 actual size, 5 contain for large image, actual size for small image, default value is 0
};
var jssor_slider1 = new $JssorSlider$("slider1_container", options);
//responsive code begin
//you can remove responsive code if you don't want the slider scales while window resizes
function ScaleSlider() {
var windowWidth = $(window).width();
if (windowWidth) {
var windowHeight = $(window).height();
var originalWidth = jssor_slider1.$OriginalWidth();
var originalHeight = jssor_slider1.$OriginalHeight();
var scaleWidth = windowWidth;
if (originalWidth / windowWidth > originalHeight / windowHeight) {
scaleWidth = Math.ceil(windowHeight / originalHeight * originalWidth);
}
jssor_slider1.$ScaleWidth(scaleWidth);
// I added this bit:
// Adjust vertical alignment
$( '.slide-image.show-bottom').each(function() {
var $this = $(this)
if ($this.height() > windowHeight) {
$this.css('margin-top', windowHeight - $this.height())
}
})
// End vertical alignment
}
else
window.setTimeout(ScaleSlider, 30);
}
ScaleSlider();
$(window).bind("load", ScaleSlider);
$(window).bind("resize", ScaleSlider);
$(window).bind("orientationchange", ScaleSlider);
//responsive code end
UPDATE:
It seems to have something to do with the height: 900px;
set on the img. That is the height jQuery finds with .height(), regardless of the actual height that jssor has applied. How do I get the actual height after jssor has scaled it? That seems to be the crux.
If anyone is interested, I was able to do what I needed with Flexslider in the end.
<section id="featured" style="height:900px">
<div class="flexslider" style="max-height: 100%;">
<ul class="slides" style="max-height: 100%;">
{% for slide in slides %}
<li>
<img style="visibility: hidden" src="{{ MEDIA_URL }}{{ slide }}" alt="{{ slide.title }}" title="{{ slide.title }}">
</li>
{% endfor %}
</ul>
</div>
</section>
<script type="text/javascript">
$('.flexslider').flexslider({options});
scaleImages();
$( window ).resize(function() {
setSliderHeight()
scaleImages()
});
function scaleImages() {
var $images = $('.flexslider .slides img');
$images.css("width", '');
$images.css("height", $('#featured').height());
setTimeout(function(){
$images.each(function(){
var $this = $(this);
var parentWidth = $this.parent().width();
var widthDelta = parentWidth - $this.width();
if (widthDelta < 0 ) {
$this.css('margin-top', '');
$this.css('margin-left', widthDelta/2);
} else {
$this.css('margin-left', '');
// scale width (to assimilate 'cover', take this out to 'contain')
if (widthDelta > 0) {
$this.height('auto')
.width(parentWidth);
var heightDelta = $('#home-featured').height() - $this.height();
$this.css('margin-top', heightDelta/2);
widthDelta = parentWidth - $this.width();
if (widthDelta < 0 ) {
$this.css('margin-left', widthDelta / 2)
}
}
}
$images.css('visibility', '');
})}, 100);
}
function setSliderHeight(){
$('#home-featured').height($(window).height());
}
setSliderHeight()
</script>
Note that rather than using 100vh, I set the height in js due to a bug in iOS with vh/vw properties on elements with dynamic content.