Галерея изображений для сайта с эффектом лупы

Здравствуйте, уважаемые читатели XoZbloga! В этом уроке мы создадим простую библиотеку JavaScript для добавления эффекта лупы при наведении на изображение. Мы сделаем все с нуля, без использования jQuery на чистом JS. Давайте приступать!

Вы могли видеть такой эффект на многих торговых сайтах, в том числе очень популярных, таких как eBay, Amazon и AliExpress. Как правило такая галерея изображений состоит из нескольких картинок-миниатюр и одного выбранного изображения, которое можно увеличить и изучить более подробно с помощью лупы при наведении.

Чтобы не усложнять библиотеку, создадим ее из одного файла JavaScript, а также дополнительного CSS-файла для стилизации галереи.

HTML разметка

Прежде чем мы начнем писать JS, давайте набросаем простейшую HTML разметку. Разметка будет включать в себя блоки для размещения миниатюр, пустой div для эффекта масштабирования, а также некоторые предопределенные классы для работы библиотеки.

1
2
3
4
5
6
7
8
<div id="my-gallery" class="vanilla-zoom">
    <div class="sidebar">
        <img src="images/image-1.jpg" class="small-preview">
        <img src="images/image-2.jpg" class="small-preview">
        <img src="images/image-3.jpg" class="small-preview">
    </div>
    <div class="zoomed-image"></div>
</div>

Вы конечно же вольны изменить этот макет и добавить столько изображений, сколько потребуется. Важно то, что каждое изображение-миниатюра должно иметь класс small-preview, а также то что этот макет должен иметь блок div с классом zoomed-image.

Конечно вся логика библиотеки будет прописана в JS-файле, но без стилей CSS не обойтись. Поэтому не забываем подключить как JS файл, так и CSS к странице с галереей:

1
2
<link rel="stylesheet" href="vanilla-zoom/vanilla-zoom.css">
<script src="vanilla-zoom/vanilla-zoom.js"></script>

Теперь, когда разметка и файл библиотеки подключены, остается только инициализировать ее.

1
2
3
<script>
    vanillaZoom.init('#my-gallery');
</script>

К слову объект vanillaZoom является глобально доступным. Объект имеет только один метод, который предназначен для инициализации плагина. Он принимает один параметр — идентификатор нашей галерее. Таким образом, мы можем иметь несколько независимых галерей, инициализированных на одной странице.

Разработка библиотеки

При создании front-end библиотек JavaScript необходимо убедиться, что правильно зарегистрирован их API. Есть много способов сделать это, возможно самым простым из них является метод от Jordan Checkman. Вкратце это сводится к следующему:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(function(window) {
  function define_library() {
    // Создадим объект библиотеки и все его свойства и методы.
    var vanillaZoom = {};
    vanillaZoom.init = function(galleryId) {
      // Вся логика библиотеки здесь.
    }
    return vanillaZoom;
  }

  // Добавим объект vanillaZoom в глобальную область, если он еще не определен.
  if(typeof(vanillaZoom) === 'undefined') {
    window.vanillaZoom = define_library();
  }
  else{
    console.log("Библиотека уже определена.");
  }
})(window);

Приведенный выше код обернут в функцию самоисполнения. Таким образом, когда мы добавляем vanilla-zoom.js файл в проект, библиотека будет автоматически зарегистрирована и объект vanillaZoom со всеми его методами будет доступен пользователю. А для того чтобы ваш проект был всегда доступен и работал на высокой скорости стоит задуматься о его размещении здесь.

В нашей библиотеке есть только один метод — vanillaZoom.init(galleryId). Его задача — выбрать элементы галереи DOM и добавить к ним обработчики событий.

Сначала мы проверяем, если соответствующие элементы были добавлены в HTML и выбираем их. Мы не можем использовать jQuery, поэтому нам приходится полагаться на собственные методы JavaScript для работы с DOM.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var container = document.querySelector(el);

if(!container) {
    console.error('Пожалуйста, укажите корректный класс галереи.');
    return;
}

var firstSmallImage = container.querySelector('.small-preview');
var zoomedImage = container.querySelector('.zoomed-image');

if(!zoomedImage) {
    console.error('Пожалуйста добавьте .zoomed-image элемент в галерею.');
    return;
}

if(!firstSmallImage) {
    console.error('Пожалуйста добавьте изображение с классом .small-preview в галерею.');
    return;
}
else {
    // Устанавливаем путь увеличиваемого изображения
    zoomedImage.style.backgroundImage = 'url('+ firstSmallImage.src +')';
}

В последней строке приведенного выше кода мы берем путь до изображения одного из миниатюр предварительного просмотра и устанавливаем его в качестве фона нашего масштабируемого элемента. Это происходит, как только vanillaZoom.init(galleryId) вызывается.

Мы сделаем то же самое, когда один будет выполнен клик по любой другой миниатюры. Это позволит пользователю выбрать, какое изображение он хочет увеличить.

1
2
3
4
5
6
7
container.addEventListener("click", function (event) {
  var elem = event.target;

  if (elem.classList.contains("small-preview")) {
      zoomedImage.style.backgroundImage = 'url('+ elem.src +')';
  }
});

К элементу zoomed-image прикреплено несколько обработчиков событий. Первый активируется при входе курсора в элемент, увеличивая размер фонового изображения, создавая эффект масштабирования.

1
2
3
zoomedImage.addEventListener('mouseenter', function(e) {
    this.style.backgroundSize = "250%";
}, false);

Поскольку изображение теперь очень большое, оно не поместится в контейнер и будет видна только его часть. Мы хотим, чтобы пользователи могли выбирать, какая часть изображения увеличивается, поэтому мы добавляем обработчик события mousemove, который изменяет положение фона.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
zoomedImage.addEventListener('mousemove', function(e) {

  // getBoundingClientReact дает нам различную информацию о положении элемента.
  var dimentions = this.getBoundingClientRect();

  // Вычисляем положение курсора внутри элемента (в пикселях).
  var x = e.clientX - dimentions.left;
  var y = e.clientY - dimentions.top;

  // Вычисляем положение курсора в процентах от общего размера элемента.
  var xpercent = Math.round(100 / (dimentions.width / x));
  var ypercent = Math.round(100 / (dimentions.height / y));

  // Обновляем положение фона изображения.
  this.style.backgroundPosition = xpercent+'% ' + ypercent+'%';

}, false);

Когда курсор покидает область увеличенного изображение необходимо, чтобы изображение вернулось в нормальное состояние. Это легко сделать, вернув размер фона cover и положение фона в center.

1
2
3
4
zoomedImage.addEventListener('mouseleave', function(e) {
    this.style.backgroundSize = "cover";
    this.style.backgroundPosition = "center";
}, false);

Вот и всё!

Поддержка браузеров

Библиотека должна работать во всех современных настольных браузерах, хотя некоторые из CSS flexbox могут не отображаться должным образом на старых IE.

К сожалению, эффект масштабирования не очень хорошо воспроизводится на сенсорных устройствах. Поэтому лучше предоставить свои изображения по-другому для мобильных устройств. В нашем CSS мы просто скрыли элемент масштабирования и расположили изображения вертикально, но вы можете попробовать другие решения, такие как карусель.

Заключение

Вы можете получить полный исходный код этой статьи, с помощью кнопки загрузки в верхней части страницы. Вы можете свободно использовать библиотеку во всех своих проектах, коммерческих или личных. Удачи в программировании!

Чтобы оставаться в курсе свежих статей и уроков подписывайтесь на еженедельную почтовую рассылку или на новостную ленту RSS. Спасибо!