画像を専用のコンテナーにラップすることで、これを回避する方法で解決することになりました。そのコンテナーの寸法は Sven の回答のように計算されますが、最終的にはブラウザーが引き継ぐことができます。このようにして、レイアウトの変更は最小限に抑えられ、最終的には画像間のわずかな時間だけ、このクレイジーなことを行うだけになります。
完全を期すために、コードの大きな塊を次に示します。
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();
}
関連する HTML:
<div class="viewer" style="display: none;">
<div class="picture"></div>
<div class="caption"><div class="caption-text"></div></div>
</div>
そしてCSS:
.viewer .picture img {
display: block;
margin: 0 auto;
max-width: 100%;
height: auto;
}
このようにして、次の画像のサイズまたは現在の画像のサイズのいずれかである画像の周りにスペースを残し、新しい画像が読み込まれる前に発生するように見える小さなサイズは決して残しません (何らかの理由で発生し続けました)。これにはおそらく何百万もの解決策があり、私のものは特に簡単ではないように感じます。