0

I'm trying to make an image fit nicely on different screen sizes without breaking the layout. The following bit of CSS helps:

.viewer .main img {
    max-width: 100%;
    height: auto;
}

But the trouble is this image changes. I use a bit of Javascript to create a new img element each time the image changes, instead of reusing the existing one. (This seems a little more reliable for what I'm doing). The browser doesn't know the image's size until it is loaded, creating an obvious flicker in the interim. I deal with that by setting the image's width and height attributes in HTML. Without the above CSS rule, that works fine.

With that CSS, the flickering is still there. For some reason, when I create a new img element, the CSS seems to be causing the browser to ignore its width and height attributes, so. It ends up as ignorant of the aspect ratio as it was before.

Here's a jsfiddle to illustrate the situation: http://jsfiddle.net/7sDtN/ One of the images in there is very very big (138 MB), so be careful if you're on a metered connection :)

What I would love is to get the image to scale according to those dimensions I set in HTML. Preferably in a nice way. A Javascript solution isn't the end of the world (I'm already using it, for course), but if there's an elegant CSS solution that would be very nice.

3
  • 3
    a picture is worth a thousand words ;) can you make a jsfiddle? Commented Jul 16, 2012 at 18:43
  • But you don't mean you set the old width and height attributes of img and want css to keep the ratio you have putted there manually instead of the original image-ratio, or? Commented Jul 16, 2012 at 18:45
  • Ooh, for some reason I didn't realize how cool jsfiddle is until now. Done! jsfiddle.net/7sDtN (Caution: one of the images is very, very big, so don't let it sit there for too long). I learned a little more about my problem, too. Seems this doesn't happen if I change the src attribute for an existing img element. It only happens when I create a new one with javascript. Commented Jul 17, 2012 at 1:58

2 Answers 2

1

I ended up solving this in a roundabout way by wrapping the image in a dedicated container, along with some strange looking javascript to keep it in place as the image loads. The dimensions for that container are calculated as in Sven's answer, but ultimately it lets the browser take over. This way layout changes are kept fairly minimal and we end up only doing this crazy stuff for the bit of time between images.

Here's a big wad of code, for completedness:

function Viewer(container) {
    var viewer = this;

    container = $(container);

    var pictureBox = $('.picture', container);
    var img = $('<img>').appendTo(pictureBox);

    var hide = function() {
        /* [snip] */
    }

    var getPictureDisplayHeight = function(picture) {
        var ratio = picture.data.h / picture.data.w;
        var displayWidth = Math.min(pictureBox.width(), picture.data.w);
        var displayHeight = Math.min(displayWidth * ratio, picture.data.h);
        return displayHeight;
    }

    var stopLoadingTimeoutId = undefined;
    var stopLoadingTimeout = function() {
        container.removeClass('loading');
    }

    var showPicture = function(picture) {
        var imgIsChanging = img.data('picture') != picture;

        container.show();

        /* This code expects to be cleaned up by stopLoadingTimeout or onImgLoaded, which will not fire if img src doesn't change */
        if (imgIsChanging) {
            container.addClass('loading');
            window.clearTimeout(stopLoadingTimeoutId);
            stopLoadingTimeoutId = window.setTimeout(stopLoadingTimeout, 3000);
        }

        pictureBox.css({
            'min-height' : pictureBox.height()
        });

        var displayHeight = getPictureDisplayHeight(picture);
        if (displayHeight > pictureBox.height()) {
            /* Grow pictureBox if necessary */
            pictureBox.stop(true, false);
            pictureBox.animate({
                'height' : displayHeight
            }, 150);
        }

        /* I wish I could set width and height here, but it causes the current image to stretch */
        img.attr({
            'src' : picture.fullPath
        }).data('picture', picture);
    }

    var onImgLoaded = function(event) {
        /* The load event might not be fired, so nothing here should be essential */

        var picture = img.data('picture');

        container.removeClass('loading');

        var displayHeight = getPictureDisplayHeight(picture);
        pictureBox.stop(true, false);
        pictureBox.animate({
            'min-height' : 0,
            'height' : displayHeight
        }, 150, function() {
            pictureBox.css('height', 'auto');
        });

        window.clearTimeout(stopLoadingTimeoutId);
    }

    var onImgClicked = function(event) {
        selectNextPicture();
    }

    var onPictureSelectedCb = function(picture) {
        if (picture) {
            showPicture(picture);
        } else {
            hide();
        }
    }

    var init = function() {
        img.on('click', onImgClicked);
        img.on('load', onImgLoaded);
    }
    init();
}

Relevant HTML:

<div class="viewer" style="display: none;">
    <div class="picture"></div>
    <div class="caption"><div class="caption-text"></div></div>
</div>

And CSS:

.viewer .picture img {
    display: block;
    margin: 0 auto;
    max-width: 100%;
    height: auto;
}

This way we leave space around the image that is either the size of the next image or the size of the current image, and never the smaller size that seems to happen before a new image is loaded (which kept happening for some reason). There are probably a million solutions to this, and mine doesn't feel especially straight-forward, so I'm certainly curious to see others :)

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

Comments

0

If I understand you right, you can achieve your goal by using the following code

HTML

<div id="Wrapper">
    <img id="MyPic" src="http://www.davidstorey.tv/wp-content/uploads/2012/05/image.php_.jpeg" alt="" width="300" height="200" />
</div>

CSS

body{
    width:100%;
}

#Wrapper{
    width:98%;
    border:1px solid red;
}

jQuery

$("document").ready(function(){
    var ratio=$("#MyPic").width() / $("#MyPic").height();
    $("#MyPic").css("width","100%");
    $("#MyPic").css("height", $("#MyPic").width()/ratio+"px");
});

Here is the link to jsfiddle

1 Comment

Okay, so that's certainly an option, but one of the problems is the Javascript happens separately from the browser dealing with layout. If I hook that up to a resize event, I get a noticeable delay where the image is stretched and weird looking before it snaps back to a sensible aspect ratio. (Hm… I've always assumed there isn't, but is there a way to change that?). It does work nicely for an iOS or Android device, though, since the only time the resizing happens there is when you rotate the screen :)

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.