Setting up 360-degree product photos in 1C-Bitrix

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 360-Photo Viewing in 1C-Bitrix

Buyer wants to examine a product from all sides before ordering. A regular 8-photo gallery doesn't replace the ability to rotate the object. 360-viewing is a sequence of frames (24-72 photos) that create rotation illusion when dragged.

Storing 360-Shot Frames

360-frames are stored like regular images linked to product via iblock property type "File" (F) with "Multiple" flag. Property created with code like IMAGES_360.

When frames added via admin, saved in b_iblock_element_property with PROPERTY_TYPE = 'F' — each frame separate row with common IBLOCK_ELEMENT_ID and IBLOCK_PROPERTY_ID.

Getting frames for frontend:

$element = \CIBlockElement::GetByID($productId)->GetNextElement();
$props   = $element->GetProperties();
$frames  = $props['IMAGES_360']['VALUE'];

// $frames — array of file IDs from b_file
$frameUrls = array_map(function($fileId) {
    return \CFile::GetPath($fileId);
}, (array)$frames);

// Pass to JSON for JS-player
echo json_encode(['frames' => $frameUrls]);

JavaScript Player for 360

Simplest implementation — Canvas or CSS background-position. three-sixty.js or js-cloudimage-360-view libraries suit production.

CSS-sprite implementation (all frames glued into one horizontal sprite):

class Viewer360 {
    constructor(el, frames) {
        this.el     = el;
        this.frames = frames;
        this.total  = frames.length;
        this.current = 0;
        this.dragging = false;
        this.startX   = 0;

        this.img = new Image();
        this.img.src = frames[0];
        el.appendChild(this.img);

        el.addEventListener('mousedown', e => { this.dragging = true; this.startX = e.clientX; });
        document.addEventListener('mouseup', () => { this.dragging = false; });
        document.addEventListener('mousemove', e => this.onMove(e));

        // Touch
        el.addEventListener('touchstart', e => { this.dragging = true; this.startX = e.touches[0].clientX; });
        document.addEventListener('touchend', () => { this.dragging = false; });
        document.addEventListener('touchmove', e => this.onMove(e.touches[0]));
    }

    onMove(e) {
        if (!this.dragging) return;
        const delta = e.clientX - this.startX;
        if (Math.abs(delta) > 5) {
            this.current = (this.current + (delta > 0 ? 1 : -1) + this.total) % this.total;
            this.img.src = this.frames[this.current];
            this.startX  = e.clientX;
        }
    }
}

Initialization in bitrix:catalog.element template:

new Viewer360(
    document.getElementById('viewer-360'),
    <?= json_encode($frameUrls) ?>
);

Frame Preloading

Lazy-loaded frames lag on first rotation. Correct approach — preload all frames in background after page load:

function preloadFrames(urls) {
    return Promise.all(urls.map(url => {
        return new Promise(resolve => {
            const img = new Image();
            img.onload = resolve;
            img.src    = url;
        });
    }));
}

preloadFrames(frameUrls).then(() => {
    // Unlock control after loading
    document.getElementById('viewer-360').classList.remove('loading');
});

Container is blocked with spinner until preload completes.

Optimization: WebP and Frame Size

36 frames × 800×600px in JPEG 80% = ~3-5 MB total. For mobile networks this is heavy. Optimization:

  • Convert to WebP via cwebp or Bitrix resize component with webp format.
  • Reduce frame resolution to 600×450 — sufficient for 360-rotation.
  • Progressive load: show first frame, load rest in background.

Bitrix generates WebP via \CFile::ResizeImageGet() with 'format' => 'webp' parameter in certain versions. Older versions use external converter via exec('cwebp ...') on upload.

Storing in Separate Iblock

For catalogs with hundreds of products with 360-sets, storing frames in main iblock property creates many b_iblock_element_property records. Alternative — separate iblock for 360-galleries linked to product via PROPERTY_TYPE = 'E' (element link). Simplifies set management and allows linking one set to multiple SKUs.