Розробка 360°-перегляду товару для інтернет-магазину

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка 360°-перегляду товару для інтернет-магазину
Середня
~2-3 робочих дні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка 360°-прегляду товару для інтернет-магазину

360°-перегляд — послідовність фотографій об'єкту, зроблених з рівномірним кроком по колу. Користувач перетягує зображення влево-вправо та бачить товар зі всіх сторін. Технічно це не відео та не 3D — просто анімація через масив статичних кадрів, але ефект створює ілюзію інтерактивного вращення.

Фотозйомка для 360°

Результат залежить від зйомки, а не від коду. Технічні вимоги:

  • Кількість кадрів: 24–72. 24 кадри = крок 15° (достатньо), 36 кадрів = крок 10° (плавно), 72 кадри = крок 5° (дуже плавно, але ~3x більше даних)
  • Поворотний стіл з рівномірним кроком (motorized turntable) — ключове обладнання
  • Освітлення: постійне, без рухомих тіней між кадрами
  • Фон: білий або прозорий (PNG з альфою), щоб вбудувати в будь-який дизайн
  • Роздільна здатність: 1000–2000px — баланс якості та ваги

Типовий обсяг: 36 кадрів × 200KB = 7MB на один товар. Це багато для сторінки. Вирішується прогресивною загрузкою.

Формати зберігання кадрів

Три варіанти:

Окремі файли: /360/product-42/frame-{01..36}.webp. Просто, прозоро для кеша CDN. При зміні кадру — браузер завантажує потрібний файл (або бере з кеша).

Спрайт (sprite sheet): всі кадри в одному зображенні (6×6 grid для 36 кадрів). Менше HTTP-запитів, але один великий файл (36 × 200KB = 7.2MB). Відображення через background-position. Підходить, якщо всі кадри потрібні одразу.

Відеофайл: кадри конвертуються в MP4 без звука, воспроизводяться через <video> з управлінням через currentTime. Найбільш компактний варіант (H.264 компресія). Управління:

const video = document.querySelector('video');
let isDragging = false;

canvas.addEventListener('mousedown', e => {
  isDragging = true;
  startX = e.clientX;
  startTime = video.currentTime;
});

canvas.addEventListener('mousemove', e => {
  if (!isDragging) return;
  const delta = (e.clientX - startX) / canvas.offsetWidth;
  video.currentTime = Math.max(0, Math.min(
    video.duration,
    startTime - delta * video.duration
  ));
});

Реалізація на окремих кадрах

Найбільш поширений підхід — масив зображень:

class Product360Viewer {
  private frames: HTMLImageElement[] = [];
  private currentFrame = 0;
  private isDragging = false;

  constructor(
    private container: HTMLElement,
    private canvas: HTMLCanvasElement,
    private frameUrls: string[]
  ) {
    this.preloadFrames();
    this.bindEvents();
  }

  private preloadFrames() {
    const loadFrame = (index: number) => {
      const img = new Image();
      img.src = this.frameUrls[index];
      img.onload = () => {
        this.frames[index] = img;
        if (index === 0) this.render(0);
        if (index < this.frameUrls.length - 1) loadFrame(index + 1);
      };
    };
    loadFrame(0);
  }

  private render(frameIndex: number) {
    const ctx = this.canvas.getContext('2d')!;
    const img = this.frames[frameIndex];
    if (!img) return;
    ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    ctx.drawImage(img, 0, 0, this.canvas.width, this.canvas.height);
  }

  private handleDrag(deltaX: number) {
    const sensitivity = 3; // пікселів на кадр
    const frameDelta = Math.round((this.startX - deltaX) / sensitivity);
    this.currentFrame = ((this.startFrame + frameDelta) % this.frameUrls.length + this.frameUrls.length) % this.frameUrls.length;
    this.render(this.currentFrame);
  }
}

Прогресивна загрузка

7MB одразу при відкритті сторінки — неприйнятно. Стратегія:

  1. Відображаємо статичне зображення (перший кадр) — він уже в галереї товару
  2. При наведенні / при попаданні в viewport (IntersectionObserver) — починаємо загрузку кадрів
  3. Показуємо індикатор загрузки «Загрузка 360°: 45%»
  4. При загрузці >50% кадрів — активуємо інтерактивність
  5. Продовжуємо завантажувати решту в фоні

Пріоритет загрузки: непарні кадри (0, 2, 4, 8, 16, 32...) — спочатку грубоа інтерактивність, потім заповнення пропусків.

Touch-события для мобайлу

private bindEvents() {
  // Mouse
  this.canvas.addEventListener('mousedown', e => this.startDrag(e.clientX));
  window.addEventListener('mousemove', e => { if (this.isDragging) this.handleDrag(e.clientX); });
  window.addEventListener('mouseup', () => this.isDragging = false);

  // Touch
  this.canvas.addEventListener('touchstart', e => {
    e.preventDefault();
    this.startDrag(e.touches[0].clientX);
  }, { passive: false });

  this.canvas.addEventListener('touchmove', e => {
    e.preventDefault();
    if (this.isDragging) this.handleDrag(e.touches[0].clientX);
  }, { passive: false });

  this.canvas.addEventListener('touchend', () => this.isDragging = false);
}

Важливо: passive: false та e.preventDefault() на touchmove — інакше браузер буде скроллити сторінку замість вращення товару.

Готові бібліотеки

Якщо завдання розова та немає спеціальних вимог:

  • 360-image-viewer (npm) — легка, vanilla JS, підтримка touch
  • Pannellum — для панорам (equirectangular), не для предметної зйомки
  • Three.js з equirectangular texture — для справжньої сферичної панорами

Для React: react-360-image-viewer — обертає базову функціональність, але кастомізація обмежена.

Автозапуск вращення

При першому попаданні в viewport — автоматично прокрутити один оборот, потім зупинитися. Це демонструє можливість та підказує користувачу, що зображення інтерактивне.

autoSpin(rotations = 1, fps = 30) {
  const totalFrames = this.frameUrls.length * rotations;
  let frame = 0;
  const interval = setInterval(() => {
    this.currentFrame = (this.currentFrame + 1) % this.frameUrls.length;
    this.render(this.currentFrame);
    if (++frame >= totalFrames) clearInterval(interval);
  }, 1000 / fps);
}

Підказки для користувача

Інтерактивність повинна бути очевидною. Стандартні рішення:

  • Іконка «360°» поверх зображення в галереї (на тумбнейлі)
  • Overlay з підказкою «Перетащите для вращения» — зникає при першому взаємодії
  • Стрілочки влево-вправо по бокам (альтернативне управління)
  • Кнопки «Пауза/Воспроизвести» для автовращения

Інтеграція з галереєю

360°-перегляд — один з слотів галереї товара. Тумбнейл — спеціальна іконка, не фото. При виборі цього слоту — запускається 360°-віджет замість звичайного зображення. При переході на інший слот — віджет демонтується та освобождає пам'ять (скасовуємо всі незавершені загрузки через AbortController).

Терміни

  • Інтеграція готової бібліотеки (react-360-image-viewer або аналог): 3–5 робочих днів
  • Кастомна реалізація (Canvas API, прогресивна загрузка, touch): 1.5–2.5 тижні
  • Настройка pipeline зйомки та конвертації (якщо немає): додаткова завдання поза розробкою
  • Пакетна конвертація існуючих кадрів (WebP, оптимізація): 2–3 дні

Вартість зйомки та підготовки контенту часто перевищує вартість розробки віджету. Це потрібно враховувати при плануванні: 36 кадрів для 500 товарів — це операційна завдача, а не технічна.