Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery Image Viewport Calculation Algorithm to Avoid Scrollbars

I am creating an image hover effect but I am having problem with it. When I hover over certain images, the scrollbars appear which I want to avoid but don't know how to do so. I believe it has to do with viewport and calculations but am not sure how that is done.

Example Here

JSBin Code

Here is the code:

$('.simplehover').each(function(){
    var $this = $(this);        
    var isrc = $this[0].src, dv = null;
                        
    $this.mouseenter(function(e){
        dv = $('<div />')
            .attr('class', '__shidivbox__')
            .css({
                display: 'none',
                zIndex : 9999,
                position: 'absolute',
                top: e.pageY + 20,
                left: e.pageX + 20
            })
            .html('<img alt="" src="' + isrc + '" />')
            .appendTo(document.body);           
        dv.fadeIn('fast');
    })
    .mouseleave(function(){
        dv.fadeOut('fast');
    });         

});

Can anyone please help me how do I make it so that hovered image appears at such place that scrollbars dont appear? (Of course we can't avoid scrollbar if image size is very very big)

I just want to show original image on zoom while avoiding scrollbars as much as possible.

Please note that I am planning to convert it into jQuery plugin and therefore I can't force users of plugin to have overflow set to hidden. The solution has do with viewport, left, top, scroll width and height, window width/height properties that I can incorporate into plugin later on.


Update:

I have come up with this:

http://jsbin.com/upuref/14

However, it is very very hacky and not 100% perfect. I am looking for a better algorithim/solution. I have seen many hover plugins that do this very nicely but since I am not that good at this, I can't do it perfectly well. For example Hover Zoom Chrome Plugin does great job of showing hovered images at appropriate place based on their size.

like image 747
Dev555 Avatar asked Sep 23 '12 13:09

Dev555


3 Answers

Like this:

html{overflow-x:hidden;}
html{overflow-y:hidden;}

All you need to do is add these definitions to your CSS and you're done.

Update with Resize: this is the mouseenter code for resizing and repositioning the pictures BOTH horizontally and vertically. Now, no matter where the HOVER image shows up, it's resized and positioned to always show in full AND uncut. As far as the scrollbars are concerned, if you show more thumbnails than can fit on the page, you will have scrollbars even before the HOVER images show up.

FINAL AND WORKING UPDATE: Because you had focused on the scrollbars being hidden, I think you overlooked the fact that if you put more thumbnails than the viewport can contain, the scrollbars would show up anyway and that therefore, since the user can scroll down the document, when you calculate the position of the hover image, not only do you need to account for the resize but you also to account for the scrollTop position too! FINAL JSBIN HERE, all pictures are showing RESIZED and in FULL no matter where the scrollTop is and no matter what the viewport size is.

$this.mouseenter(function () {

    dv = $('<div />')
          .attr('class', '__shidivbox__')
          .css({
            'display': 'none',
            'z-index': 9999,
            'position': 'absolute',
            'box-shadow': '0 0 1em #000',
            'border-radius': '5px'
          })
          .html('<img alt="" src="' + isrc + '" />')
          .appendTo(document.body);

    var DocuWidth = window.innerWidth;
    var DocuHeight = window.innerHeight;

    var DvImg = dv.find('img');

    var TheImage = new Image();
    TheImage.src = DvImg.attr("src");

    var DivWidth = TheImage.width;
    var DivHeight = TheImage.height;

    if (DivWidth > DocuWidth) {

        var WidthFactor = (DivWidth / DocuWidth) + 0.05;
        DivHeight = parseInt((DivHeight / WidthFactor), 10);
        DivWidth = parseInt((DivWidth / WidthFactor), 10);
    }

    var ThumbHeight = $this.height();
    var ThumbWidth = $this.width();
    var ThumbTop = $this.position().top;
    var ThumbLeft = $this.position().left;

    var SpaceAboveThumb = ThumbTop - $(document).scrollTop();
    var SpaceBelowThumb = DocuHeight - ThumbTop - ThumbHeight + $(document).scrollTop();

    var MaxHeight = Math.max(SpaceAboveThumb, SpaceBelowThumb);

    if (DivHeight > MaxHeight) {

        var HeightFactor = (DivHeight / MaxHeight) + 0.05;
        DivHeight = parseInt((DivHeight / HeightFactor), 10);
        DivWidth = parseInt((DivWidth / HeightFactor), 10);
    }

    var HoverImgLeft = 0;
    var HoverImgTop = 0;

    if (SpaceBelowThumb > SpaceAboveThumb) {
        HoverImgTop = ThumbTop + ThumbHeight;
    } else {
        HoverImgTop = ThumbTop - DivHeight;
    }

    HoverImgTop = (HoverImgTop < 0) ? 0 : HoverImgTop;

    HoverImgLeft = (DocuWidth - DivWidth) / 2;

    dv.find('img').css({
        'width': DivWidth,
        'height': DivHeight,
        'border-radius': '5px'
    });

    dv.css({
        'left': HoverImgLeft,
        'top': HoverImgTop
    });

    dv.fadeIn('fast');
});
like image 180
12 revs Avatar answered Oct 06 '22 11:10

12 revs


Well, this looks fun. Anyway, here's my answer. I've been watching this for a few days and though I'd chip in too. The following will make sure that the hovering images do not go out of the viewport and in the event that the width of the image is bigger than the available space for display, the display of the image will be resized (You can comment out the code that does this if you don't want it. Just look for the word "resize" in the code).

var $document = $(document);
$('.simplehover').each(function(){
    var $this = $(this);
    // make sure that element is really an image
    if (! $this.is('img')) return false;

    var isrc = $this[0].src, ibox = null;

    if (! isrc) return false;
    ibox = $('<img />')
            .attr('class', 'simpleimagehover__shidivbox__')
            .css({
                display: 'none',
                zIndex : 99,
                MozBoxShadow: '0 0 1em #000', 
                WebkitBoxShadow: '0 0 1em #000',
                boxShadow: '0 0 1em #000',
                position: 'absolute',
                MozBorderRadius : '10px',
                WebkitBorderRadius : '10px',
                borderRadius : '10px'
            })
            .attr('src', isrc)
            .appendTo(document.body);          

    $this.bind('mouseenter mousemove', function(e) {
        $('.simpleimagehover__shidivbox__').hide();

        var left = e.pageX + 5, 
            top = e.pageY + 5,
            ww = window.innerWidth,
            wh = window.innerHeight,
            w = ibox.width(),
            h = ibox.height(),
            overflowedW = 0,
            overflowedH = 0;

        // calucation to show element avoiding scrollbars as much as possible - not a great method though
        if ((left + w + $document.scrollLeft()) > ww)
        {
            overflowedW = ww - (left + w + $document.scrollLeft());
            if (overflowedW < 0)
            {
               left -= Math.abs(overflowedW);
            }
        }

        // 25 is just a constant I picked arbitrarily to compensate pre-existing scrollbar if the page itself is too long
        left -= 25;
        left = left < $document.scrollLeft() ? $document.scrollLeft() : left;

        // if it's still overflowing because of the size, resize it
        if (left + w > ww)
        {
            overflowedW = left + w - ww;
            ibox.width(w - overflowedW - 25);
        }


        if (top + h > wh + $document.scrollTop())
        {
            overflowedH = top + h - wh - $document.scrollTop();
            if (overflowedH > 0)
            {
                top -= overflowedH;
            }
        }

        top = top < $document.scrollTop() ? $document.scrollTop() : top;
        ibox.css({
            top: top,
            left: left
        });

        ibox.show();
    }); 


    $('.simpleimagehover__shidivbox__').mouseleave(function(){
        $('.simpleimagehover__shidivbox__').hide();
    });

    $document.click(function(e){
        $('.simpleimagehover__shidivbox__').hide();
    });

    $document.mousemove(function(e){
        if (e.target.nodeName.toLowerCase() === 'img') {
            return false;
        }

        $('.simpleimagehover__shidivbox__').hide();
    });
});

While my solution itself is not perfect, you might find something useful in there that can help you determine the viewport. Also, you might want to think about the performance of the code. Since this is a plugin that you're building, you'll want to make some optimizations before releasing it to public. Basically, just make sure it's not slow.

like image 21
Kemal Fadillah Avatar answered Oct 06 '22 12:10

Kemal Fadillah


You can position the image based on the available width: http://jsbin.com/upuref/19/

This demo takes in account the available space for positioning the image (i.e. the window width minus the image width). Also I've improved the event order, with the popup div only starting its fade-in after the image has been loaded.

like image 20
lanzz Avatar answered Oct 06 '22 12:10

lanzz