Setting Up Zoom Viewing of Product Images in 1C-Bitrix
Buyer wants to examine serial number on label or fabric texture. Standard Bitrix product card component shows fixed-size image. Zoom added at template level without core changes.
Product Images in Catalog
Main product image stored in iblock property type F with code MORE_PHOTO (multiple) or in PREVIEW_PICTURE / DETAIL_PICTURE fields of b_iblock_element table — file IDs from b_file.
For zoom need original high-resolution image. Problem: standard Bitrix resize reduces images via \CFile::ResizeImageGet(). Original stays in b_file with full path.
Get both paths:
$detailPictureId = $arResult['DETAIL_PICTURE']['ID'];
// Original (for zoom)
$originalPath = \CFile::GetPath($detailPictureId);
// Preview (400x400)
$previewInfo = \CFile::ResizeImageGet($detailPictureId, ['width' => 400, 'height' => 400], BX_RESIZE_IMAGE_PROPORTIONAL, true);
$previewPath = $previewInfo['src'];
Zoom via CSS Transform
Simplest zoom without libraries — CSS cursor: zoom-in + transform: scale on click. Works for basic needs:
.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;
}
});
In component template:
<div class="product-image-zoom">
<img src="<?= $previewPath ?>"
data-original-src="<?= $originalPath ?>"
alt="<?= htmlspecialchars($arResult['NAME']) ?>">
</div>
Lens Zoom (Magnifying Glass Effect)
Professional solution — "lens": on hover over thumbnail, magnified area appears nearby. Drift library — 3KB no dependencies:
import Drift from 'drift-zoom';
new Drift(document.querySelector('.product-image-zoom img'), {
paneContainer: document.querySelector('.zoom-pane'),
inlinePane: false,
zoomFactor: 3,
hoverBoundingBox: true,
});
For Drift, image needs data-zoom attribute with high-resolution image URL:
<img src="<?= $previewPath ?>"
data-zoom="<?= $originalPath ?>"
alt="Product">
<div class="zoom-pane"></div>
Original Image Requirements
Zoom works well only if original image has sufficient resolution. With 3x scaling, 400×400 image gives 133×133px original lens — blurry. Recommended minimum for zoomFactor: 3 — original 1600×1600px.
If originals arrive in low resolution, disable zoom conditionally:
$fileInfo = \CFile::GetFileArray($detailPictureId);
$showZoom = ($fileInfo['WIDTH'] >= 1200 && $fileInfo['HEIGHT'] >= 1200);
Gallery with Zoom
For cards with multiple images (MORE_PHOTO) zoom applies to active image. On slide switch reinitialize zoom instance with new URL:
function switchImage(url, originalUrl) {
mainImg.src = url;
mainImg.dataset.zoom = originalUrl;
if (window.driftInstance) {
window.driftInstance.destroy();
}
window.driftInstance = new Drift(mainImg, driftOptions);
}
Initialize zoom after image load or wrap in DOMContentLoaded. On touchscreen devices hover doesn't work — need separate tap-to-zoom logic or fullscreen via Fancybox with data-fancybox="gallery" on each gallery element.







