Search code examples
jquerycssflexsliderjssor

jssor slider: vertical-align images – flexslider used instead


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.


Solution

  • 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.