Гид по технологиям

Лупа для изображений: как сделать на JavaScript

• 5 min read • Frontend • Обновлено 10 Dec 2025
Лупа для изображений на JavaScript
Лупа для изображений на JavaScript

Лупа для изображений — лёгкий способ дать пользователю возможность увеличивать фрагменты фото прямо на странице. В статье описан пошаговый пример реализации на чистом HTML/CSS/JS, инструкции по настройке, проверочные критерии и рекомендации по адаптации под разные сценарии.

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

Увеличительное стекло поверх рук на ноутбуке.

О чём эта статья

  • Что такое лупа для изображений и где она полезна
  • Пошаговое создание компонента с примерами кода
  • Критерии приёмки и тестовые сценарии
  • Подсказки по производительности и альтернативы

Зачем нужна лупа для изображений

Лупа даёт пользователю возможность подробно рассмотреть часть изображения без перехода в полноэкранный режим. Это полезно:

  • Для интернет-магазинов, где покупатель оценивает качество и детали товара.
  • Для фотогалерей и портфолио, где важны части кадра.
  • Для карт, схем, технических иллюстраций.

Преимущества:

  • Улучшает понятность изображений.
  • Помогает пользователю принять решение о покупке.
  • Уменьшает число возвратов за счёт точного представления товара.

Когда не стоит использовать лупу:

  • Если изображения уже связаны с интерактивным 3D-просмотром или высокодетализированным viewer’ом.
  • Для маленьких декоративных иконок — увеличение не даст смысла.

Файловая структура и лицензия

Код примера свободен и может распространяться по MIT-лицензии. Создайте папку проекта и добавьте файлы:

  • index.html
  • style.css
  • main.js

Ниже приведён минимально полный рабочий пример. Вы можете менять тексты, пути к изображениям и стили.

HTML: базовая разметка

Этот HTML обеспечивает заголовок, инструкцию по управлению и контейнер для изображения с лупой. В примере локализованы тексты интерфейса — это удобно при копировании проекта в русскоязычные продукты.





  
  
  Лупа для изображений
  



  

Лупа для изображений

Нажимайте Arrow Up или Arrow Down чтобы увеличить или уменьшить уровень зума. Уровень зума: 1

CSS: внешний вид и базовая позиция

Стиль задаёт размер контейнера, фон и оформление самой лупы. Переменные CSS позволяют легко менять размер лупы.

:root {
  --magnifier-width: 150;
  --magnifier-height: 150;
}

body {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.container {
  width: 400px;
  height: 300px;
  background-size: cover;
  background-image: url("https://cdn.pixabay.com/photo/2019/03/27/15/24/animal-4085255_1280.jpg");
  background-repeat: no-repeat;
  position: relative;
  cursor: none;
}

.magnifier {
  border-radius: 400px;
  box-shadow: 0px 11px 8px 0px #0000008a;
  position: absolute;
  width: calc(var(--magnifier-width) * 1px);
  height: calc(var(--magnifier-height) * 1px);
  cursor: none;
  background-image: url("https://cdn.pixabay.com/photo/2019/03/27/15/24/animal-4085255_1280.jpg");
  background-repeat: no-repeat;
}

span {
  display: block;
}

.header {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.hidden {
  visibility: hidden;
}

div > span:nth-child(3) {
  font-size: 20px;
}

JavaScript: логика лупы

В JS мы получаем элементы, отслеживаем позицию курсора, управляем уровнем зума и обновляем фон лупы. Ниже — аккуратно оформленный и исправленный пример, готовый к использованию.

let magnifier = document.querySelector(".magnifier");
let container = document.querySelector(".container");

// Читаем размеры лупы как числа (px)
let magnifierWidth = parseFloat(getComputedStyle(magnifier).width);
let magnifierHeight = parseFloat(getComputedStyle(magnifier).height);

let zoomLevelLabel = document.querySelector(".zoom-level");
let zoom = 2; // начальный множитель
let maxZoomLevel = 5;
let pointerX;
let pointerY;
let magnifyX;
let magnifyY;

function getZoomLevel() {
  return zoom;
}

function getPointerPosition() {
  return { x: pointerX, y: pointerY };
}

function updateMagImage() {
  let evt = new MouseEvent("mousemove", {
    clientX: getPointerPosition().x,
    clientY: getPointerPosition().y,
    bubbles: true,
    cancelable: true,
    view: window,
  });

  container.dispatchEvent(evt);
}

window.addEventListener("keyup", (e) => {
  if (e.key === "ArrowUp" && Number(zoomLevelLabel.textContent) < maxZoomLevel) {
    zoomLevelLabel.textContent = +zoomLevelLabel.textContent + 1;
    zoom = zoom + 0.3;
    updateMagImage();
  }

  if (e.key === "ArrowDown" && Number(zoomLevelLabel.textContent) > 1) {
    zoomLevelLabel.textContent = +zoomLevelLabel.textContent - 1;
    zoom = zoom - 0.3;
    updateMagImage();
  }
});

container.addEventListener("mousemove", (e) => {
  magnifier.classList.remove("hidden");
  let rect = container.getBoundingClientRect();

  // Используем clientX/Y относительно контейнера
  let x = e.clientX - rect.left;
  let y = e.clientY - rect.top;

  // Центрируем лупу так, чтобы курсор был в середине
  let translateX = x - magnifierWidth / 2;
  let translateY = y - magnifierHeight / 2;

  magnifier.style.transform = `translate(${translateX}px, ${translateY}px)`;
  const imgWidth = rect.width;
  const imgHeight = rect.height;

  magnifier.style.backgroundSize =
    imgWidth * getZoomLevel() + "px " + imgHeight * getZoomLevel() + "px";

  // Позиционирование фона так, чтобы видимый центр совпадал с курсором
  magnifyX = x * getZoomLevel() - magnifierWidth / 2;
  magnifyY = y * getZoomLevel() - magnifierHeight / 2;

  magnifier.style.backgroundPosition = -magnifyX + "px " + -magnifyY + "px";
});

container.addEventListener("mouseout", () => {
  magnifier.classList.add("hidden");
});

window.addEventListener("mousemove", (e) => {
  pointerX = e.clientX;
  pointerY = e.clientY;
});

Скриншот лупы в действии на примере товара

Пояснения к коду и подсказки

  • Мы используем background-image в контейнере и в лупе, чтобы совпадало растровое содержимое. Это простая и совместимая техника.
  • Для адаптивности можно динамически читать rect.width/height — в примере это сделано в обработчике mousemove.
  • Значение zoom хранится как множитель. Удобно синхронизировать его с числами в интерфейсе (zoom-level).
  • Применение transform: translate обеспечивает плавное позиционирование и работает аппаратно ускоренно.

Советы по улучшению:

  • Добавьте плавное появление/исчезание лупы через transition для лучшего UX.
  • Поддержите сенсорные устройства: на мобильных покажите кнопку «Увеличить» для полноэкранного просмотра, т.к. hover не всегда доступен.
  • Загружайте отдельную, более высокомасштабную версию изображения для лупы, если исходник мал в разрешении.

Критерии приёмки

  1. Лупа появляется при наведении курсора внутрь контейнера.
  2. Лупа скрывается при выходе курсора из контейнера.
  3. Фон внутри лупы корректно показывает увеличенную область, соответствующую положению курсора.
  4. Клавиши Arrow Up/Arrow Down изменяют уровень зума, интерфейсный индикатор обновляется.
  5. На разных стационарных разрешениях позиционирование остаётся точным.

Роль‑ориентированные чеклисты

Разработчик:

  • Проверить, что background-image совпадает в контейнере и в .magnifier.
  • Убедиться, что размеры читаются динамически и не жёстко заданы.
  • Тестировать на edge-cases: быстрый mousemove, ресайз окна.

Дизайнер:

  • Подобрать форму и тень лупы, чтобы она не заслоняла критичные зоны интерфейса.
  • Решить поведение для мобильных (другой паттерн увеличения).

QA:

  • Проверить клавиатурную доступность и отсутствие препятствий для пользователей с ограничениями.
  • Убедиться, что низкое разрешение источника не ломает UX.

Тестовые сценарии и приёмочные кейсы

  • Навести курсор в центр изображения — лупа показывает увеличение соответствующего фрагмента.
  • Перемещать курсор по краям — убедиться, что лупа не уезжает за границы контейнера некорректно.
  • Быстро нажимать Arrow Up/Arrow Down — зум изменяется без ошибок и без исключений в консоли.
  • Отключить фон-изображение в контейнере — лупа должна скрываться или показывать корректную заглушку.

Производительность и оптимизация

  • Не создавайте тяжелые пер-кадровые операции внутри mousemove. Ограничьте вычисления и используйте requestAnimationFrame, если нужно оптимизировать при высокой нагрузке.
  • Используйте отдельное изображение высокого разрешения только внутри лупы, чтобы не загружать сразу тяжелые ресурсы для всех пользователей.
  • Для множества миниатюр применяйте делегирование событий, а не подключайте обработчики к каждому элементу.

Альтернативные подходы

  • Использовать canvas: даёт полный контроль над рендерингом и позволяет применять фильтры и пиксельные эффекты. Но требует больше кода.
  • Использовать SVG: удобно для векторной графики и масштабирования без потерь.
  • Подключить готовую библиотеку (например, крупные open-source плагины) — быстрее внедряется, но увеличивает вес проекта и может переизбыточно покрывать требования.

Когда лупа не подойдёт

  • Когда нужен многопользовательский viewer с синхронизацией изменений (требуется сложная логика).
  • Для мобильных устройств без hover лучше предоставить альтернативный UI, например, полноэкранный viewer или pinch-to-zoom.

Мини‑методология внедрения (пошагово)

  1. Выбрать элемент(ы)-источники изображений и убедиться в доступности высококачественной версии.
  2. Добавить стили контейнера и лупы, настроить размеры через CSS-переменные.
  3. Подключить скрипт и протестировать в десктопных браузерах.
  4. Добавить адаптивный сценарий для мобильных.
  5. Запустить A/B тест, если нужно измерить влияние на конверсии.

Галерея крайних случаев

  • Малое исходное изображение: лупа покажет пикселизацию — рекомендуется загружать отдельное hi-res изображение.
  • Скролл страницы: убедиться, что координаты рассчитываются относительно clientX/clientY и rect, а не pageX без учёта скролла.
  • Быстрое перемещение мыши: поведение остаётся предсказуемым, если вычисления оптимизированы.

Скриншот результата работы лупы с большим товаром

Короткое резюме

Лупа для изображений — недорогой по усилиям, но мощный UX-компонент. Она повышает доверие пользователя, помогает лучше рассмотреть детали и может положительно влиять на конверсию. Реализация на чистом JavaScript проста и легко настраивается. При внедрении учитывайте доступность и мобильные сценарии.

Ключевые выводы

  • Лупа улучшает восприятие товара и служит мостом между статичной картинкой и физическим осмотром.
  • Для десктопа hover-лупа — оптимальный вариант; для мобильных нужны альтернативы.
  • Следите за производительностью и используйте более качественные изображения только при необходимости.

Краткое объявление для продукта: новая лупа для изображений теперь улучшает просмотр товаров и повышает доверие покупателей за счёт детальной визуализации.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Несколько аккаунтов Skype: Multi Skype Launcher
Программное обеспечение

Несколько аккаунтов Skype: Multi Skype Launcher

Журнал для работы: повысить продуктивность
Productivity

Журнал для работы: повысить продуктивность

Персональные звуки уведомлений на Android
Android.

Персональные звуки уведомлений на Android

Скачивание шоу Hulu для офлайн‑просмотра
Стриминг

Скачивание шоу Hulu для офлайн‑просмотра

Microsoft Start: персонализированная новостная лента
Новости

Microsoft Start: персонализированная новостная лента

Как изменить имя в Epic Games быстро
Гайды

Как изменить имя в Epic Games быстро