Частые запросы и запросы, которые я получаю и которые я сам являюсь разработчиком, звучат так:
«Как я могу преобразовать одно изображение в другое?».
В частности, Натану Ригли из pictureandword.com понадобился метод, который бы плавно переходил одно изображение в другое при событии при наведении курсора мыши, а затем медленно исчезал, когда мышь перемещала изображение.
Ролловеры изображений были основным ядром JavaScript 90-х, и для многих известных мне разработчиков JavaScript это была одна из отправных точек, которые привели к их страсти к языку JavaScript. Сегодня ролловеры не представляют никакой сложности, будь то с помощью CSS или самого простого JavaScript:
$(function () {
$('img.swap').hover(function () {
this.src="images/sad.jpg";
}, function () {
this.src="images/happy.jpg";
});});
Сегодняшняя проблема — переход на новый ролловер!
Смотрите полный скринкаст ( альтернативная версия flash )
(Версия QuickTime составляет около 20 Мб, флэш-версия потоковая)
Как подойти к проблеме
Есть несколько разных способов решения этой проблемы (и я хотел бы услышать альтернативные методы в комментариях ).
Вот различные подходы, которые я собираюсь пройти:
- Два изображения
- Одиночное изображение
- Чистый CSS
Ключ ко всем этим методам заключается в том, как устроена визуализированная разметка (то есть, что в конечном итоге видит браузер): все они очень похожи. По сути, конечное изображение для перехода должно находиться absolute
в том же положении, что и начальное изображение. Также стоит иметь в виду, что изображения, между которыми мы растекаемся, должны быть одинакового размера (по высоте и ширине).
Примечание: все три из этих методов имеют оговорку: стилизация начального или конечного изображения может привести к нарушению эффекта. Я бы порекомендовал обернуть изображение в div
или span
и стилизовать этот элемент, так как это потребует меньше изменений в JavaScript. В любом случае: всегда лучше тестировать в целевых браузерах.
Техника двух изображений
Я должен начать с зачисления Карла Сведберга, который руководит Learning jQuery . Он решил проблему перехода Натана, используя следующую технику. Метод Карла начинается с двух изображений в разметке: начального и конечного изображений. Они содержатся в div
и конечное изображение содержится в дальнейшем div
с absolute
позиционированием.
Важно отметить, что этот метод работает лучше всего для абсолютно позиционных изображений. Изменение div.fade
чтобы position: relative
означает div
элемент остается в качестве block
элемента, и div
будет растягиваться по ширине это элемент контейнера (недобросовестный до 100%).
Посмотреть рабочий пример и источник
HTML
<div class="fade">
<a href="/info.html"><img src="start.jpg" /></a>
<div>
<a href="/info.html"><img src="end.jpg" /></a>
</div>
</div>
CSS
Obviously if I had more than one fading image, I would use an ID or alternative class to position the top
and left
CSS properties.
.fade {
position: absolute;
top: 100px
left: 100px
}
.fade div {
position: absolute;
top: 0;
left: 0;
display: none;
}
jQuery
// when the DOM is ready:
$(document).ready(function () {
// find the div.fade elements and hook the hover event
$('div.fade').hover(function() {
// on hovering over, find the element we want to fade *up*
var fade = $('> div', this);
// if the element is currently being animated (to a fadeOut)...
if (fade.is(':animated')) {
// ...take it's current opacity back up to 1
fade.stop().fadeTo(250, 1);
} else {
// fade in quickly
fade.fadeIn(250);
}
}, function () {
// on hovering out, fade the element out
var fade = $('> div', this);
if (fade.is(':animated')) {
fade.stop().fadeTo(3000, 0);
} else {
// fade away slowly
fade.fadeOut(3000);
}
});
});
Single Image Technique
This takes the two image technique further. I like the idea that we should let JavaScript add the sugar to the markup — in that we should really only want an image tag, and using some method, know which image we want to fade to.
This technique allows us to insert the image in the markup as we would if there were no transition effect, and the image can be inline, rather being positioned absolutely. We are going to use the background-image
CSS property to specify the target image to fade to.
View the working example and the source
HTML
<img class="fade"
src="images/who.jpg"
style="background: url(images/who_ro.jpg);" />
CSS
Other than the inline background image — none is required. You can also apply the background-image
using classes if you like. If we wanted to absolutely position the image, or float: right
for instance, the best way to do this (if we want to keep the transition), would be to wrap it in a div
and style that element.
jQuery
Using jQuery, we execute the following tasks:
- Wrap the image in a
span
- Insert a new image, whose source is the
background-image
of our start image - Position the new image so that sits directly behind the starting image
- Bind the hover event to start the effect
// create our transition as a plugin
$.fn.crossfade = function () {
return this.each(function () {
// cache the copy of jQuery(this) - the start image
var $$ = $(this);
// get the target from the backgroundImage + regexp
var target = $$.css('backgroundImage').replace(/^url|[\(\)]/g, ''));
// nice long chain: wrap img element in span
$$.wrap('<span style="position: relative;"></span>')
// change selector to parent - i.e. newly created span
.parent()
// prepend a new image inside the span
.prepend('<img>')
// change the selector to the newly created image
.find(':first-child')
// set the image to the target
.attr('src', target);
// position the original image
$$.css({
'position' : 'absolute',
'left' : 0,
// this.offsetTop aligns the image correctly inside the span
'top' : this.offsetTop
});
// note: the above CSS change requires different handling for Opera and Safari,
// see the full plugin for this.
// similar effect as single image technique, except using .animate
// which will handle the fading up from the right opacity for us
$$.hover(function () {
$$.stop().animate({
opacity: 0
}, 250);
}, function () {
$$.stop().animate({
opacity: 1
}, 3000);
});
});
};
// Not only when the DOM is ready, but when the images have finished loading,
// important, but subtle difference to $(document).ready();
$(window).bind('load', function () {
// run the cross fade plugin against selector
$('img.fade').crossfade();
});
Pure CSS Technique
If I’m honest, this final technique is a bit cheeky — but still valid. It uses CSS animations currently only available in Safari 3 (and WebKit). However, this is a great example of how to the leverage CSS using an iPhone, in place JavaScript. The HTML is the same rendered HTML from the single image technique — but it requires zero JavaScript.
View the working example and the source (to see the effect, view using Safari 3).
HTML
<span style="position: relative;">
<img src="images/who_ro.jpg" />
<img
style="position: absolute; left: 0px;"
src="images/who.jpg" class="fade" />
</span>
CSS
Although this is only supported in Safari 3, the roll over still works in Firefox (and could work in IE7 — though not IE6 because :hover
only works on anchors) — because it’s changing the image’s opacity on :hover
.
img.fade {
opacity: 1;
-webkit-transition: opacity 1s linear;
}
img.fade:hover {
opacity: 0;
}
Taking it Further
I’ve taken the single image technique further in to a complete plugin. It’s designed to allows us to pass options to control the type of bind, delays, callbacks and tests before running the animation.
You can see the plugin in action in this simple memory game I put together quickly. It pulls the latest photos from flickr, shuffles them, and then sets your memory skills to work. It’s obviously just a quick prototype — and I’m not sure what happens when you go beyond level 5! Enjoy.