Реализация слайдера/карусели на сайте

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

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

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

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация слайдера/карусели на сайте
Простая
~1 рабочий день
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • 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

Реализация слайдера/карусели на сайте

Слайдер — один из самых злоупотребляемых UI-элементов и одновременно источник боли с производительностью. Нативный CSS Scroll Snap решает 70% случаев без JS. Для остального — Swiper.js.

CSS Scroll Snap: без JS

<div class="slider" role="region" aria-label="Слайдер">
  <div class="slider__track">
    <div class="slider__slide" id="slide-1">
      <img src="/images/slide1.webp" alt="Слайд 1" loading="eager">
    </div>
    <div class="slider__slide" id="slide-2">
      <img src="/images/slide2.webp" alt="Слайд 2" loading="lazy">
    </div>
    <div class="slider__slide" id="slide-3">
      <img src="/images/slide3.webp" alt="Слайд 3" loading="lazy">
    </div>
  </div>
</div>
.slider {
  overflow: hidden;
}

.slider__track {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;       /* Firefox */
}

.slider__track::-webkit-scrollbar {
  display: none;               /* Chrome/Safari */
}

.slider__slide {
  flex: 0 0 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;    /* не пропускать слайды при быстром свайпе */
}

.slider__slide img {
  width: 100%;
  height: 400px;
  object-fit: cover;
  display: block;
}
// Навигация через JS (опционально)
function goToSlide(index: number) {
  const track = document.querySelector('.slider__track')!
  const slide = track.children[index] as HTMLElement
  slide.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' })
}

// Определяем текущий слайд через IntersectionObserver
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const index = Array.from(entry.target.parentElement!.children).indexOf(entry.target)
      updateDots(index)
    }
  })
}, { root: document.querySelector('.slider__track'), threshold: 0.5 })

document.querySelectorAll('.slider__slide').forEach(slide => observer.observe(slide))

Swiper.js: полноценная карусель

npm install swiper
import { Swiper, SwiperSlide } from 'swiper/react'
import { Navigation, Pagination, Autoplay, A11y, Thumbs, FreeMode } from 'swiper/modules'
import { useState } from 'react'
import 'swiper/css'
import 'swiper/css/navigation'
import 'swiper/css/pagination'
import 'swiper/css/thumbs'

interface Slide {
  src: string
  alt: string
  caption?: string
  link?: string
}

export function HeroSlider({ slides }: { slides: Slide[] }) {
  return (
    <Swiper
      modules={[Navigation, Pagination, Autoplay, A11y]}
      slidesPerView={1}
      navigation={{
        prevEl: '.swiper-btn-prev',
        nextEl: '.swiper-btn-next',
      }}
      pagination={{ clickable: true, dynamicBullets: true }}
      autoplay={{ delay: 5000, disableOnInteraction: true, pauseOnMouseEnter: true }}
      loop={true}
      grabCursor={true}
      a11y={{
        prevSlideMessage: 'Предыдущий слайд',
        nextSlideMessage: 'Следующий слайд',
        paginationBulletMessage: 'Перейти к слайду {{index}}',
      }}
      keyboard={{ enabled: true }}
      speed={600}
      className="hero-slider"
    >
      {slides.map((slide, i) => (
        <SwiperSlide key={i}>
          <figure className="hero-slider__slide">
            {slide.link
              ? <a href={slide.link}><img src={slide.src} alt={slide.alt} loading={i === 0 ? 'eager' : 'lazy'} /></a>
              : <img src={slide.src} alt={slide.alt} loading={i === 0 ? 'eager' : 'lazy'} />
            }
            {slide.caption && <figcaption>{slide.caption}</figcaption>}
          </figure>
        </SwiperSlide>
      ))}
      <button className="swiper-btn-prev" aria-label="Предыдущий">‹</button>
      <button className="swiper-btn-next" aria-label="Следующий">›</button>
    </Swiper>
  )
}

Карусель товаров с брейкпоинтами

export function ProductCarousel({ products }: { products: Product[] }) {
  return (
    <Swiper
      modules={[Navigation, FreeMode]}
      navigation
      freeMode={{ enabled: true, sticky: false }}
      slidesPerView={1.2}
      spaceBetween={16}
      breakpoints={{
        480: { slidesPerView: 2.2, spaceBetween: 16 },
        768: { slidesPerView: 3.2, spaceBetween: 20 },
        1024: { slidesPerView: 4,   spaceBetween: 24, freeMode: { enabled: false } },
        1280: { slidesPerView: 5,   spaceBetween: 24 },
      }}
      grabCursor={true}
    >
      {products.map(product => (
        <SwiperSlide key={product.id}>
          <ProductCard product={product} />
        </SwiperSlide>
      ))}
    </Swiper>
  )
}

Gallery с thumbnails

export function GalleryWithThumbs({ images }: { images: string[] }) {
  const [thumbsSwiper, setThumbsSwiper] = useState<any>(null)

  return (
    <div className="gallery-with-thumbs">
      <Swiper
        modules={[FreeMode, Navigation, Thumbs]}
        thumbs={{ swiper: thumbsSwiper && !thumbsSwiper.destroyed ? thumbsSwiper : null }}
        spaceBetween={10}
        navigation={true}
        className="gallery-main"
      >
        {images.map((src, i) => (
          <SwiperSlide key={i}>
            <img src={src} alt={`Фото ${i + 1}`} loading={i === 0 ? 'eager' : 'lazy'} />
          </SwiperSlide>
        ))}
      </Swiper>

      <Swiper
        onSwiper={setThumbsSwiper}
        modules={[FreeMode, Thumbs]}
        spaceBetween={8}
        slidesPerView={5}
        freeMode={true}
        watchSlidesProgress={true}
        className="gallery-thumbs"
      >
        {images.map((src, i) => (
          <SwiperSlide key={i}>
            <img src={src} alt={`Миниатюра ${i + 1}`} loading="lazy" />
          </SwiperSlide>
        ))}
      </Swiper>
    </div>
  )
}

Производительность: предотвращение CLS

Без явных размеров слайдер вызывает Layout Shift при инициализации:

/* Резервируем высоту до загрузки JS */
.swiper {
  aspect-ratio: 16/9;  /* или конкретная высота */
}

.swiper-slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Скелетон до инициализации */
.swiper:not(.swiper-initialized) {
  background: #f1f5f9;
}

.swiper:not(.swiper-initialized) .swiper-slide:not(:first-child) {
  display: none;
}

Autoplay с паузой на hover и focus

// Swiper встроенная пауза работает для hover
// Добавляем паузу для доступности при фокусе на слайде
const swiper = document.querySelector('.swiper')?.swiper

swiper?.el.addEventListener('focusin', () => swiper.autoplay.stop())
swiper?.el.addEventListener('focusout', () => swiper.autoplay.start())

Сроки

CSS Scroll Snap слайдер с навигацией — 3–4 часа. Swiper с брейкпоинтами, автоплеем и кастомной навигацией — 1 день. Сложная карусель с thumbnails, lazy-загрузкой, предотвращением CLS и accessibility — 1.5–2 дня.