Реализация галереи изображений (Lightbox) на сайте

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация галереи изображений (Lightbox) на сайте
Простая
~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

Реализация галереи изображений (Lightbox) на сайте

Галерея с lightbox — один из самых частых UI-компонентов. Задача кажется простой, пока не сталкиваешься с touch-свайпами, lazy-загрузкой, zoom, предзагрузкой соседних изображений и доступностью.

Библиотеки

PhotoSwipe 5 — лучший вариант для большинства задач. Чистый vanilla JS, touch-first, zoom, поддержка srcset. Активно поддерживается, ~20 KB gzip.

GLightbox — легковесный (~12 KB), поддерживает видео и iframe в лайтбоксе, без зависимостей.

Fancybox 5 — богатый функционал, но платный для коммерческих проектов.

Для простых случаев без zoom и видео — GLightbox. Для полноценной фотогалереи — PhotoSwipe.

PhotoSwipe: интеграция

npm install photoswipe
import PhotoSwipeLightbox from 'photoswipe/lightbox'
import 'photoswipe/style.css'
import { useEffect, useRef } from 'react'

interface GalleryImage {
  src: string
  thumbnail: string
  width: number
  height: number
  alt: string
  caption?: string
}

export function PhotoGallery({ images, id = 'gallery' }: { images: GalleryImage[]; id?: string }) {
  const galleryRef = useRef<HTMLElement>(null)

  useEffect(() => {
    if (!galleryRef.current) return

    const lightbox = new PhotoSwipeLightbox({
      gallery: `#${id}`,
      children: 'a',
      pswpModule: () => import('photoswipe'),
      // Предзагрузка соседних
      preload: [1, 2],
      // Анимация
      showHideAnimationType: 'zoom',
      // Закрытие по клику на фон
      closeOnVerticalDrag: true,
      // Зум
      maxZoomLevel: 4,
      initialZoomLevel: 'fit',
      secondaryZoomLevel: 1.5,
    })

    // Кастомный caption
    lightbox.on('uiRegister', () => {
      lightbox.pswp?.ui?.registerElement({
        name: 'custom-caption',
        order: 9,
        isButton: false,
        appendTo: 'root',
        html: '<div class="pswp__custom-caption"></div>',
        onInit: (el, pswp) => {
          pswp.on('change', () => {
            const currSlideElement = pswp.currSlide?.data.element
            const caption = currSlideElement?.querySelector('figcaption')?.textContent ?? ''
            el.querySelector('.pswp__custom-caption')!.textContent = caption
          })
        },
      })
    })

    lightbox.init()
    return () => lightbox.destroy()
  }, [id, images])

  return (
    <section
      id={id}
      ref={galleryRef as any}
      className="photo-gallery"
      aria-label="Галерея фотографий"
    >
      {images.map((img, i) => (
        <figure key={i} className="photo-gallery__item">
          <a
            href={img.src}
            data-pswp-width={img.width}
            data-pswp-height={img.height}
          >
            <img
              src={img.thumbnail}
              alt={img.alt}
              loading="lazy"
              decoding="async"
              width={img.width}
              height={img.height}
            />
          </a>
          {img.caption && <figcaption>{img.caption}</figcaption>}
        </figure>
      ))}
    </section>
  )
}

Адаптивные изображения через srcset

// Генерируем srcset для лайтбокса — разные размеры для разных экранов
function buildSrcSet(baseUrl: string, sizes: number[]): string {
  return sizes.map(w => `${baseUrl}?w=${w} ${w}w`).join(', ')
}

// В PhotoSwipe 5 — через dataSource
const dataSource = images.map(img => ({
  src: img.src,
  srcset: buildSrcSet(img.src, [800, 1200, 1920, 2560]),
  width: img.width,
  height: img.height,
  alt: img.alt,
  // Для мобильных — не грузить 2560px
  msrc: img.thumbnail,  // placeholder до загрузки полного
}))

Masonry-лейаут сетки

/* CSS Columns — простейшее masonry без JS */
.photo-gallery {
  columns: 3 200px;
  column-gap: 8px;
}

.photo-gallery__item {
  break-inside: avoid;
  margin-bottom: 8px;
}

.photo-gallery__item img {
  width: 100%;
  height: auto;
  display: block;
}

@media (max-width: 768px) {
  .photo-gallery { columns: 2 150px; }
}

@media (max-width: 480px) {
  .photo-gallery { columns: 1; }
}

Для поддержки CSS Masonry (пока только в Firefox за флагом) или точного выравнивания рядов — Masonry.js или native CSS grid с grid-template-rows: masonry.

Равномерная сетка с known dimensions

/* Если соотношение сторон известно заранее */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 8px;
}

.gallery-grid__item {
  aspect-ratio: 4/3;
  overflow: hidden;
}

.gallery-grid__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.3s ease;
}

.gallery-grid__item:hover img {
  transform: scale(1.05);
}

Lazy-загрузка с placeholder

import { useState } from 'react'

function LazyGalleryImage({ src, thumbnail, alt, width, height }: GalleryImage) {
  const [loaded, setLoaded] = useState(false)
  const [error, setError] = useState(false)

  return (
    <div className={`gallery-image ${loaded ? 'gallery-image--loaded' : ''}`}>
      {/* LQIP (Low Quality Image Placeholder) */}
      {!loaded && !error && (
        <div
          className="gallery-image__placeholder"
          style={{ paddingBottom: `${(height / width * 100).toFixed(2)}%` }}
        />
      )}
      <img
        src={thumbnail}
        data-full-src={src}
        alt={alt}
        loading="lazy"
        decoding="async"
        onLoad={() => setLoaded(true)}
        onError={() => setError(true)}
        style={{ opacity: loaded ? 1 : 0, transition: 'opacity 0.3s' }}
      />
    </div>
  )
}

GLightbox для видео-галереи

npm install glightbox
import GLightbox from 'glightbox'
import 'glightbox/dist/css/glightbox.css'

const lightbox = GLightbox({
  elements: [
    {
      href: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
      type: 'video',
      source: 'youtube',
      width: 900,
    },
    {
      href: '/video/promo.mp4',
      type: 'video',
      description: 'Промо-ролик',
    },
    {
      href: '/images/photo1.jpg',
      type: 'image',
      description: 'Описание фото',
    },
  ],
  autoplayVideos: false,
  loop: false,
  draggable: true,
  dragToleranceX: 40,
  dragToleranceY: 65,
  swipeToClose: true,
})

Keyboard navigation и accessibility

// PhotoSwipe поддерживает клавиатуру нативно
// Дополнительно: возврат фокуса при закрытии
lightbox.on('close', () => {
  // Возвращаем фокус на тот элемент, с которого открыли
  const opener = document.querySelector('[data-gallery-opener]') as HTMLElement
  opener?.focus()
})

// ARIA для сетки
// role="list" + role="listitem" или просто нативный <ul><li>
<ul class="photo-gallery" role="list" aria-label="Галерея проектов">
  <li role="listitem">
    <a href="/large/1.jpg"
       aria-label="Открыть изображение: Проект офиса, 2024"
       data-pswp-width="2400"
       data-pswp-height="1600">
      <img src="/thumb/1.jpg" alt="Проект офиса, 2024" loading="lazy">
    </a>
  </li>
</ul>

Сроки

GLightbox с базовой сеткой и CSS columns — половина дня. PhotoSwipe с masonry, srcset, caption, и кастомным UI — 1.5–2 дня. Полноценная галерея с загрузкой из API, пагинацией, фильтрами по категориям и видео поддержкой — 4–5 дней.