
Кросс-браузерное определение реальных размеров изображения с помощью jQuery
📅 Опубликовано:22 мая 2011
Это статья из старого первого блога, который сохранился только на archive.org. Все старые статьи помечены тегом 🚨 старое
Кросс-браузерное определение реальных размеров изображения с помощью jQuery и вписывание его в область с заранее заданными размерами. Должно получиться следующе
Итак, дано: на странице имеются уже загруженные изображения немаленького размера.
Необходимо: узнать реальный размер каждого изображения и отмасштабировать его, то есть задать соответствующую ширину или высоту, чтобы изображение уместилось в заранее заданные размеры, к примеру в область 100х100 пикселей. Дабы можно было взглядом охватить как можно больше изображений, и уже при необходимости, по клику их увеличить.
Знаю, что грузить сразу и много изображений, потом незаметно их уменьшать на клиенте — полный маразм, но так получилось, но сейчас не об этом :)
HTML прост:
HTML
<a class="link link--info" href="images/img-1.jpg" class="img-link">
<img src="images/img-1.jpg" alt="" class="img" width="100" height="100" />
</a>
<a class="link link--info" href="images/img-2.jpg" class="img-link">
<img src="images/img-2.jpg" alt="" class="img" width="100" height="100" />
</a>
<a class="link link--info" href="images/img-N.jpg" class="img-link">
<img src="images/img-N.jpg" alt="" class="img" width="100" height="100" />
</a>
...
<a class="link link--info" href="images/img-20.jpg" class="img-link zoom-img">
<img src="images/img-20.jpg" alt="" class="img" width="100" height="100" />
</a>
Так как информация о размере изображения нам будет доступна, только когда это изображение браузер загрузит — будем использовать событие .load()
. По этому событию с изображением будут выполняться все необходимые процедуры:
JavaScript
// Берем все необходимые нам изображения.
var $img = $('.img');
// Дожидаемся загрузки изображения браузером.
$img.load(function(){
// Здесь будет много разных букв.
});
Если у изображения будут заданы ширина или высота атрибутами, либо в CSS — «реальными» размерами будут именно эти размеры, поэтому обезопасим себя, в самом начале удалив все эти заранее заданные размеры, если конечно они имеются:
JavaScript
// Кладем указатель на изображение в переменную,
// дабы каждый раз не вызывать функцию и не нагружать дополнительно браузер.
var CurImg = $(this);
// Если заданы атрибуты width и height - удаляем их.
CurImg.removeAttr("width")
.removeAttr("height")
// А также перестраховываемся насчет CSS-свойств.
.css({ width: "", height: "" });
С помощью методов .width()
и .height()
, выясняем размеры изображения:
JavaScript
// Получаем реальные ширину и высоту.
var width = CurImg.width();
var height = CurImg.height();
По условию наше изображение должно вписываться в область 100×100 пикселей, поэтому напишем соответствующие условия задания атрибутов для пропорционального масштабирования:
JavaScript
// Если изображение больше допустимой ширины
// или высоты (maxSize), задаем ее соответствующими атрибутами.
var maxSize = 100;
if (width > height) CurImg.attr('width', maxSize);
else if (height > width) CurImg.attr('height', maxSize);
else if (height == width) CurImg.attr('height', maxSize);
Атрибут здесь каждому изображению задается всего один — либо ширина либо высота, чтобы изображение масштабировалось пропорционально.
Изображение должно располагаться по центру контейнера, которым в данном случае будет являться ссылка. Причем как по вертикали, так и по горизонтали.
Насчет горизонтального выравнивания все просто, изображению мы зададим margin:auto;
А вот с вертикальным придется помудрить.
Для нормальных браузеров можно обойтись двумя правилами для контейнера: display: table-cell;
и vertical-align: middle;
. То есть заставить ссылку вести себя как ячейка таблицы.
Но для милых сердцу эксплореров, если не хочется мудрить с несколькими дополнительными обертками, придется писать экспрешен. Вычислять высоту контейнера, то есть ссылки, брать высоту изображения, отнимать от высоты контейнера и результат делить на два, потом задавать вычисленный отступ сверху нашему изображению.
Но мы пойдем простым путем и сразу для всех запишем там же, в нашей функции, следующий кодик:
JavaScript
// Вычисляем и задаем необходимый отступ сверху
// у изображения для того чтобы отцентрировать его по вертикали.
var imgHeight = CurImg.height();
var marginTop = (maxSize - imgHeight)/2;
CurImg.css('margin-top', marginTop)
Пока мы манипулируем с изображением, которое уже загружено в браузер, изображения на долю секунды будут размером 100×100 пикселей, потом когда удалятся атрибуты — реального размера, что будет «разрывать» нашу страницу, затем только отмасштабируются. Думаю пользователя пугать не стоит и логичнее было бы сразу задать изображениям: display:none;
А затем в функции, после всех манипуляций выставить display:block;
.
Сразу после выполнения всех необходимых действий над изображением, добавим разметку для отображения инфо-значка увеличения изображения <span class=“zoom-icon”></span>
, который будет показываться по ховеру на ссылке. В итоге вся конструкция будет выглядеть следующим образом:
JavaScript
// Берем все необходимые нам изображения.
var $img = $('.img');
// Дожидаемся загрузки изображения браузером.
$img.load(function(){
// Кладем указатель на изображение в переменную, дабы каждый раз
// не вызывать функцию и не нагружать дополнительно браузер.
var CurImg = $(this);
// Если заданы атрибуты width и height - удаляем их.
CurImg.removeAttr("width")
.removeAttr("height")
// А также перестраховываемся насчет CSS-свойств.
.css({ width: "", height: "" });
// Получаем реальные ширину и высоту.
var width = CurImg.width();
var height = CurImg.height();
// Если изображение больше допустимой ширины или высоты (maxSize),
// задаем ее соответствующими атрибутами.
var maxSize = 100;
if (width > height) CurImg.attr('width', maxSize);
else if (height > width) CurImg.attr('height', maxSize);
else if (height == width) CurImg.attr('height', maxSize);
// Вычисляем и задаем необходимый отступ сверху у изображения
// для того чтобы отцентрировать его по вертикали.
var imgHeight = CurImg.height();
var marginTop = (maxSize - imgHeight)/2;
CurImg.css('margin-top', marginTop)
// Показываем отмасштабированное изображение.
.css('display', 'block');
// Добавляем разметку для отображения значка по ховеру.
CurImg.parent().append('<span class="zoom-icon"></span>');
});
Все это будет расположено после HTML с изображениями.
В области head создадим объект изображение, тем самым подгрузив предварительно анимированный значок-предзагрузчик, который будет задан ссылке бэкграундом, чтобы пользователь не пугался пустого места и ему было не скучно :) на время манипуляций с изображениями:
JavaScript
// Создаем объект Image, тем самым заставляя браузер
// предварительно загрузить анимированное изображение прелоадера.
preloaderImg = new Image();
preloaderImg.src="images/preloader.gif";
Немного ниже расположим код вызова плагина fancybox, для красивого всплывания полноразмерного изображения, по клику. Конструкция в области head будет выглядеть так:
JavaScript
// Создаем объект Image, тем самым заставляя браузер
// предварительно загрузить анимированное изображение прелоадера.
preloaderImg = new Image();
preloaderImg.src="images/preloader.gif";
$(document).ready(function() {
$(".img-link").fancybox({
'titlePosition' : 'inside',
'transitionIn' : 'none',
'transitionOut' : 'none',
'centerOnScroll':true,
'overlayOpacity':0.2,
'titleShow': false,
'transitionIn': 'elastic',
'transitionOut': 'elastic',
'overlayColor':'#000',
'hideOnContentClick':true
});
});
СSS оформление:
CSS
.img-link {
width:100px;
height:100px;
border:1px solid #ccc;
margin:10px;
display:block;
float:left;
position:relative;
background:#fff url("images/preloader.gif") center center no-repeat;
overflow:hidden;
}
.zoom-icon {
background:url("images/zoom.png") left top no-repeat;
cursor:pointer;
height:101px;
left:-999px;
position:absolute;
top:17px;
width:104px;
z-index:100;
}
a.img-link:hover {background:#fff;}
a.img-link:hover .zoom-icon {left:17px;}
.img {display:none; margin:auto;}
И вуаля.
Но если эту страницу открыть в браузере Opera, все будет не совсем замечательно. Первый раз, когда изображения загрузятся с сервера, наша функция сработает. Но вот если затем нажать энтэр в строке браузера, либо опять перейти на эту же страницу по ссылке&nbps;— функция уже не сработает, и на странице так и останутся уныло вертеться двадцать прелоадеров :)
Связано это с тем, что наша любимая опера по-своему относится к изображениям загружающимся из кэша, и jQuery событие .load()
не срабатывает для закешированных изображений. Но стоит очистить кэш и функция срабатывает снова. Хотя она срабатывает даже если просто нажать кавишу F5. Странно, но решение все же нашлось, в комментариях к методу .load()
добрый человек предложил следующее:
JavaScript
$('.img').one('load',function(){
// Необходимые манипуляции с изображением.
})
.each(function(){
if(this.complete) $(this).trigger('load');
});
То есть сначала проверить с помощью «чистого» яваскрипт (this.complete
) доступность изображения, а потом по триггеру — загрузку.
В опере прекрасно работает, но только с версии 11, возможно и 10 (проверял в 11.11). Opera 9 (проверял в 9.63) напрочь воспротивилась выполнять функцию даже с пустым кэшем. В остальных браузерах, а также в IE6 работает прекрасно.
Конструкция целиком:
JavaScript
// Берем все необходимые нам изображения.
var $img = $('.img');
// В случае загрузки изображения единожды по триггеру
// (условие в самом низу) выполняем необходимые действия.
$('.img').one('load',function(){
// Кладем указатель на изображение в переменную, дабы каждый раз
// не вызывать функцию и не нагружать дополнительно браузер.
var CurImg = $(this);
// Если заданы атрибуты width и height - удаляем их.
CurImg.removeAttr("width")
.removeAttr("height")
// А также перестраховываемся насчет CSS-свойств.
.css({ width: "", height: "" });
// Получаем реальные ширину и высоту.
var width = CurImg.width();
var height = CurImg.height();
// Если изображение больше допустимой ширины или высоты (maxSize),
// задаем ее соответствующими атрибутами.
var maxSize = 100;
if (width > height) CurImg.attr('width', maxSize);
else if (height > width) CurImg.attr('height', maxSize);
else if (height == width) CurImg.attr('height', maxSize);
// Вычисляем и задаем необходимый отступ сверху у изображения
// для того чтобы отцентрировать его по вертикали.
var imgHeight = CurImg.height();
var marginTop = (maxSize - imgHeight)/2;
CurImg.css('margin-top', marginTop)
// Показываем отмасштабированное изображение.
.css('display', 'block');
// Добавляем разметку для отображения значка по ховеру.
CurImg.parent().append('<span class="zoom-icon"></span>');
})
// Собственно сам триггер, проверяем каждое изображение
// на "готовность" и ставим триггер на загрузку.
.each(function(){
if(this.complete) $(this).trigger('load');
});
Рабочий пример можно посмотреть здесь.
Скачать архив (архив пухлый из за специально не оптимизированных тяжелых изображений).