Настройка zoom-просмотра изображений товаров 1С-Битрикс
Покупатель хочет рассмотреть серийный номер на этикетке или текстуру ткани. Стандартный компонент карточки товара Битрикса показывает изображение фиксированного размера. Зум добавляется на уровне шаблона компонента без изменения ядра.
Изображения товара в каталоге
Основное изображение товара хранится в свойстве инфоблока типа F с кодом MORE_PHOTO (множественное) или в полях PREVIEW_PICTURE / DETAIL_PICTURE таблицы b_iblock_element — ID файлов из b_file.
Для зума нужна оригинальная версия изображения в высоком разрешении. Проблема: стандартный ресайз Битрикса уменьшает изображения при сохранении через \CFile::ResizeImageGet(). Оригинал остаётся в b_file с полным путём.
Получение пути к оригиналу и ресайзу:
$detailPictureId = $arResult['DETAIL_PICTURE']['ID'];
// Оригинал (для зума)
$originalPath = \CFile::GetPath($detailPictureId);
// Превью для отображения (400x400)
$previewInfo = \CFile::ResizeImageGet($detailPictureId, ['width' => 400, 'height' => 400], BX_RESIZE_IMAGE_PROPORTIONAL, true);
$previewPath = $previewInfo['src'];
Zoom через CSS transform
Простейший зум без библиотек — CSS cursor: zoom-in + transform: scale при клике. Подходит для базовых нужд:
.product-image-zoom {
overflow: hidden;
cursor: zoom-in;
position: relative;
}
.product-image-zoom img {
transition: transform 0.3s ease;
transform-origin: var(--zoom-x, 50%) var(--zoom-y, 50%);
}
.product-image-zoom.zoomed img {
transform: scale(2.5);
cursor: zoom-out;
}
const container = document.querySelector('.product-image-zoom');
const img = container.querySelector('img');
container.addEventListener('mousemove', function (e) {
if (!container.classList.contains('zoomed')) return;
const rect = container.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width * 100).toFixed(1) + '%';
const y = ((e.clientY - rect.top) / rect.height * 100).toFixed(1) + '%';
container.style.setProperty('--zoom-x', x);
container.style.setProperty('--zoom-y', y);
});
container.addEventListener('click', function () {
container.classList.toggle('zoomed');
if (container.classList.contains('zoomed')) {
img.src = img.dataset.originalSrc;
}
});
В шаблоне компонента:
<div class="product-image-zoom">
<img src="<?= $previewPath ?>"
data-original-src="<?= $originalPath ?>"
alt="<?= htmlspecialchars($arResult['NAME']) ?>">
</div>
Lens-зум (эффект лупы)
Более профессиональное решение — «линза»: при наведении на миниатюру рядом появляется увеличенная область. Реализуется через Drift — 3КБ без зависимостей:
import Drift from 'drift-zoom';
new Drift(document.querySelector('.product-image-zoom img'), {
paneContainer: document.querySelector('.zoom-pane'),
inlinePane: false,
zoomFactor: 3,
hoverBoundingBox: true,
});
Для Drift изображение должно иметь атрибут data-zoom с URL изображения высокого разрешения:
<img src="<?= $previewPath ?>"
data-zoom="<?= $originalPath ?>"
alt="Товар">
<div class="zoom-pane"></div>
Требования к исходным изображениям
Зум работает хорошо только если оригинальное изображение имеет достаточное разрешение. При масштабировании 3x изображение 400×400 даст лупу из 133×133 пикселей оригинала — нечётко. Рекомендуемый минимум для zoomFactor: 3 — оригинал 1600×1600px.
Если оригиналы поступают в малом разрешении, зум отключается условно:
$fileInfo = \CFile::GetFileArray($detailPictureId);
$showZoom = ($fileInfo['WIDTH'] >= 1200 && $fileInfo['HEIGHT'] >= 1200);
Галерея с зумом
Для карточек с множественными изображениями (MORE_PHOTO) зум применяется к активному изображению. При переключении слайда нужно переинициализировать зум-инстанс с новым URL:
function switchImage(url, originalUrl) {
mainImg.src = url;
mainImg.dataset.zoom = originalUrl;
if (window.driftInstance) {
window.driftInstance.destroy();
}
window.driftInstance = new Drift(mainImg, driftOptions);
}
Инициализация зума до загрузки изображения вызывает смещения — new Drift() нужно вызывать после img.onload или оборачивать в DOMContentLoaded. На тачскрин-устройствах hover не работает — нужна отдельная логика tap-to-zoom или переход к полноэкранному просмотру через Fancybox с data-fancybox="gallery" на каждом элементе галереи.







