Setting up a magnifying glass for 1C-Bitrix product images

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1212
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    815
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    565
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    980

Setting Up Magnifier (Image Zoom Loupe) in 1C-Bitrix

Magnifier works only when source image is large enough. Stores upload 800×800 photos, enable magnifier, user sees blurry pixelated square on hover — because library tries zooming image in insufficient resolution. Problem is not JavaScript but how Bitrix stores and resizes images.

Two Images: Display and Zoom Version

Magnifier libraries (EasyZoom, Drift, jQuery Zoom) work one way: <img> tag contains small version, data-zoom or parent element's href contains large. Without large version, loupe zooms same small image — unacceptable result.

Bitrix has two fields for each iblock element image: PREVIEW_PICTURE and DETAIL_PICTURE. For magnifier need third — original without resize. Standard catalog.element component passes images through CFile::ResizeImageGet(), saving reduced copy to /upload/resize_cache/. Original stays in /upload/iblock/.

Template must pass both paths:

$fileId = $arResult['DETAIL_PICTURE']['ID'];
$fileInfo = \CFile::GetFileArray($fileId);

// Reduced for display
$resized = \CFile::ResizeImageGet($fileId, ['width' => 600, 'height' => 600], BX_RESIZE_IMAGE_PROPORTIONAL);

// Original for zoom
$original = \CHTTP::URN2URI($fileInfo['SRC']);

Resolution Threshold Setup

Running magnifier on 600×600 image pointless — zoom effect appears only with scaling coefficient less than one. Real threshold: original must be 2× larger than displayed. Check in template:

$zoomEnabled = ($fileInfo['WIDTH'] >= 1200 && $fileInfo['HEIGHT'] >= 1200);

If smaller — don't output data-zoom attribute, library doesn't initialize for this element. User doesn't see broken loupe.

Connecting Library via Bitrix Assets

Connect library via \Bitrix\Main\Page\Asset, not hardcode <script> in template — prevents duplication with multiple components on page:

\Bitrix\Main\Page\Asset::getInstance()->addJs('/local/js/drift.min.js');
\Bitrix\Main\Page\Asset::getInstance()->addCss('/local/css/drift-basic.min.css');

Initialize via inline-script at template end:

document.querySelectorAll('.product-zoom-trigger').forEach(function(el) {
    new Drift(el.querySelector('img'), {
        paneContainer: document.querySelector('.product-zoom-pane'),
        zoomFactor: 3,
        hoverBoundingBox: true,
    });
});

Gallery with Multiple Images

When product has multiple photos (MORE_PHOTO), loupe must recreate on active image change. Typical error — initialize new Drift() once on load. Clicking another thumbnail — instance stays bound to old <img>, new element already in DOM.

Correct: store instance in variable, call driftInstance.destroy() before creating new on each photo switch.

Mobile Devices

On touch screens magnifier useless — hover doesn't work. Need pinch-to-zoom. PhotoSwipe solves both: desktop opens fullscreen zoom view, mobile supports gestures. Use Drift and PhotoSwipe simultaneously: Drift for pointer: fine (mouse), PhotoSwipe for pointer: coarse (touch). Detect via CSS media query @media (pointer: coarse) and JS window.matchMedia().