Делаем свой слайдер для картинок «было — стало»

Делаем свой слайдер для картинок «было — стало»

Космический проект

12 июля 2022 года телескоп Webb прислал первое полноценное фото чужих галактик в высоком разрешении. Чтобы оценить, насколько продвинулись технологии, сделаем слайдер «было — стало» и сравним новое фото со старым от телескопа Хаббл.

Логика проекта

Чтобы сделать такое, нам нужно расположить две фотографии на странице в одном и том же месте, только одну сделать как бы поверх другой. Когда перемещается слайдер, на самом деле мы изменяем координаты правой стороны левой картинки (которая выше) — так кажется, что одно фото превращается в другое.

Для этого мы сделаем так:

  1. Создаём на странице общий контейнер для картинок и по одному для каждой по отдельности — это сделаем в HTML-файле.
  2. Рисуем рамки и разделитель с кругляшом посередине — за это отвечают стили.
  3. Добавляем обработчик сдвига разделителя — он поменяет границу правого фото в соответствии с положением слайдера. Вынесем это в файл скрипта.
  4. Чтобы проект работал на смартфонах, добавим обработку нажатия на экран, чтобы слайдер плавно переезжал на место касания. То же сделаем в скрипте.

Создаём страницу

На странице всё стандартно: подключаем файл со стилями, добавляем jQuery, а в конце не забываем прописать путь к скрипту. Но кое-что новое тоже есть: мы ещё подключаем библиотеку jQuery UI для работы с интерфейсом. Нас интересует обработчик перетаскивания элементов — чтобы не писать свой код работы со слайдером, используем готовую библиотеку.

Также мы используем сокращённый вариант написания тега <img> — у него нет парного закрывающего тега, вместо него используется слеш перед скобкой.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- подключаем стили -->
  <link rel="stylesheet" type="text/css" href="style.css">
  <!-- jQuery -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"> </script> 
  <!-- и библиотеку стандартных реакций jQuery -->
  <script src="draggable-jquery-ui-min.js"> </script> 
  <title>Слайдер «было—стало»</title>
</head>
<body>
  <!-- общий контейнер для слайдера -->
  <div class="sl-container">
    <!-- картинка слева -->
    <div class="view view-after">
      <img src="webb.jpg"/>      
    </div>
    <!-- картинка справа -->
    <div class="view view-before">
      <img src="hubble.jpg"/>
    </div>
    <!-- слайдер посередине -->
    <div class="dragme">
      <!-- кругляш на слайдере -->
      <div class="dr-circle"></div>
    </div>
  </div>
  <!-- подключаем наш скрипт -->
  <script src="script.js"> </script> 
</body>
</html>
Сейчас пока две картинки в полном размере стоят одна над другой. Сейчас это исправим в стилях

Делим картинку пополам и оформляем слайдер

Чтобы квадратная картинка всегда занимала максимальную площадь окна браузера, пойдём на хитрость: найдём, что меньше, высота или ширина окна, и используем это как наш размер. В этом нам поможет CSS-команда min () — она находит минимальное значение среди аргументов. Теперь, даже если мы растянем браузер на всю ширину или высоту, картинка подстроится под новые размеры.

Мы используем 90vh вместо 100vh, чтобы не появлялись полосы прокрутки — у нас ещё есть отступы от края картинки, которым тоже нужно предусмотреть место на экране. Попробуйте ради интереса везде в коде число 90 заменить на 100 и посмотрите, что получится.

/* общие настройки для всего контейнера */
.sl-container {
    /* рисуем границу */
    border: 3px solid #BFE2FF;
    /* добавляем отступы */
    margin: 5px auto;
    /* то, что не помещается в блок — скрываем */
    overflow: hidden;
    /* используем относительное позиционирование */
    position: relative;
    /* устанавливаем высоту и ширину блока */
    width: min(90vh, 90vw);
    height: min(90vh, 90vw);
    /* ограничиваем всё размерами блока */
    box-sizing: border-box;
}

/* настройки блоков с картинками */
.sl-container .view {
    /* включаем абслютное позиционирование */
    position: absolute;
    /* сдвигаем всё в левый верхний угол */
    top: 0;
    left: 0;
    /* то, что не помещается в блоке, — скрываем */
    overflow: hidden;
}

/* настройки картинок в блоке .view */
.sl-container .view img {
    /* ширина картинок совпадает с шириной блока */
    width: min(90vh, 90vw);
}

Картинки теперь помещаются на экране, но одна закрыла другую — мы не видим фото с нового телескопа. А всё потому, что мы не разместили их на разной виртуальной высоте. Смысл в том, чтобы сделать новое фото поверх другого и обрезать его слева — так мы получим эффект «было — стало»:

/* настройки картинки слева */
.sl-container .view-after {
    /* показываем только половину */
    width: min(45vh, 45vw);
    /* и виртуально сдвигаем её ближе к зрителю*/
    z-index: 200;
}

Теперь всё выглядит красиво, но ещё нужен слайдер, чтобы можно было за него ухватиться мышкой и подвигать влево-вправо. Сделаем его из двух частей — вертикальной полоски и кругляша посередине. Кругляш сделаем из прямоугольника, у которого все углы скруглённые на 100% — это проще, чем рисовать круг.

/* настройки слайдера */
.sl-container .dragme {
    /* включаем абслютное позиционирование */
    position: absolute;
    /* ширина разделителя */
    width: 3px;
    /* на всю высоту блока */
    height: 100%;
    /* начинается сверху блока */
    top: 0px;
    /* делит картинки пополам, потому что общая ширина у них 90vh */
    left: min(45vh, 45vw);
    /* цвет фона */
    background-color: #BFE2FF;
    /* меняем форму курсора */
    cursor: pointer;
    /* поднимаем слайдер выше картинок */
    z-index: 300;
}

/* кругляш на слайдере */
.sl-container .dr-circle {
    /* включаем абсолютное позиционирование */
    position: absolute;
    /* высчитываем позицию сверху, чтобы поставить его посередине */
    top: calc(50% - 20px);
    /* сдвигаем влево, так как круг рисуется слева направо, а не из центра */
    left: -15px;
    /* превращаем фигуру в круг */
    border-radius: 100%;
    /* высота и ширина */
    width: 40px;
    height: 40px;
    /* цвет фона */
    background-color: #BFE2FF;
    /* меняем форму курсора */
    cursor: pointer;
    /* делаем на той же виртуальной высоте, что и полоска слайдера */
    z-index: 300;
}

Пишем скрипт

Визуально у нас всё красиво, но ничего подвигать нельзя — для этого нужно написать скрипт. Он будет состоять из трёх частей: реакции на сдвиг, клик по картинке и анимации сдвига при клике. Обработка клика нужна для того, чтобы проект работал на смартфонах — не все они поддерживают нужные возможности jQuery и могут обработать сдвиг слайдера. Вместо этого мы будем получать координаты клика и плавно сдвигать туда слайдер.

(function($) {
    // получаем доступ к элементу слайдера на странице
    var $dragMe = $(".dragme"),
    // к слайдеру
    $container = $(".sl-container"),
    // и картинке слева
    $viewAfter = $(".view-after");
    // используем свойство draggable из библиотеки с интерфейсом, чтобы получить координаты сдвига слайдера
    $dragMe.draggable({
        containment: "parent",
        drag: function() {
            // при перемещении слайдера меняем ширину картинки слева в стилях
            $viewAfter.css({
                width : parseFloat($(this).css('left')) + 5
            });
        }
    });
    // добавляем реакцию на клик по картинке
    $container.on("click", function(event) {
        // получаем координаты клика, чтобы сместить туда слайдер
        var eventLeft = event.pageX - $container.offset().left - 15;
        // плавно сдвигаем слайдер
        animateTo(eventLeft);
    });
    // функция сдвига слайдера при клике, на входе получаем новые координаты границы картинок
    function animateTo(_left) {
        // анимируем сдвиг слайдера
        $dragMe.animate({
            left: _left
        }, 'slow', 'linear');
        // и картинки
        $viewAfter.animate({
            width: _left
        }, 'slow', 'linear');
    }
})(jQuery);

Теперь всё работает как нужно и мы можем оценить качество съёмки нового телескопа по сравнению со старым.

Посмотреть работу слайдера на странице проекта.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- подключаем стили -->
  <link rel="stylesheet" type="text/css" href="style.css">
  <!-- jQuery -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"> </script> 
  <!-- и библиотеку стандартных реакций jQuery -->
  <script src="draggable-jquery-ui-min.js"> </script> 
  <title>Слайдер «было—стало»</title>
</head>
<body>
  <!-- общий контейнер для слайдера -->
  <div class="sl-container">
    <!-- картинка слева -->
    <div class="view view-after">
      <img src="webb.jpg"/>      
    </div>
    <!-- картинка справа -->
    <div class="view view-before">
      <img src="hubble.jpg"/>
    </div>
    <!-- слайдер посередине -->
    <div class="dragme">
      <!-- кругляш на слайдере -->
      <div class="dr-circle"></div>
    </div>
  </div>
  <!-- подключаем наш скрипт -->
  <script src="script.js"> </script> 
</body>
</html>

/* общие настройки для всего контейнера */
.sl-container {
    /* рисуем границу */
    border: 3px solid #BFE2FF;
    /* добавляем отступы */
    margin: 5px auto;
    /* то, что не помещается в блок, — скрываем */
    overflow: hidden;
    /* используем относительное позиционирование */
    position: relative;
    /* устанавливаем высоту и ширину блока */
    width: min(90vh, 90vw);
    height: min(90vh, 90vw);
    /* ограничиваем всё размерами блока */
    box-sizing: border-box;
}

/* настройки блоков с картинками */
.sl-container .view {
    /* включаем абслютное позиционирование */
    position: absolute;
    /* сдвигаем всё в левый верхний угол */
    top: 0;
    left: 0;
    /* то, что не помещается в блоке — скрываем */
    overflow: hidden;
}

/* настройки картинок в блоке .view */
.sl-container .view img {
    /* ширина картинок совпадает с шириной блока */
    width: min(90vh, 90vw);
}


/* настройки картинки слева */
.sl-container .view-after {
    /* показываем только половину */
    width: min(45vh, 45vw);
    /* и виртуально кладём её выше, чем картинку справа */
    z-index: 200;
}

/* настройки слайдера */
.sl-container .dragme {
    /* включаем абслютное позиционирование */
    position: absolute;
    /* ширина разделителя */
    width: 3px;
    /* на всю высоту блока */
    height: 100%;
    /* начинается сверху блока */
    top: 0px;
    /* делит картинки пополам, потому что общая ширина у них 90vh */
    left: min(45vh, 45vw);
    /* цвет фона */
    background-color: #BFE2FF;
    /* меняем форму курсора */
    cursor: pointer;
    /* поднимаем слайдер выше картинок */
    z-index: 300;
}

/* кругляш на слайдере */
.sl-container .dr-circle {
    /* включаем абсолютное позиционирование */
    position: absolute;
    /* высчитываем позицию сверху, чтобы поставить его посередине */
    top: calc(50% - 20px);
    /* сдвигаем влево, так как круг рисуется слева направо, а не из центра */
    left: -15px;
    /* превращаем фигуру в круг */
    border-radius: 100%;
    /* высота и ширина */
    width: 40px;
    height: 40px;
    /* цвет фона */
    background-color: #BFE2FF;
    /* меняем форму курсора */
    cursor: pointer;
    /* делаем на той же виртуальной высоте, что и полоска слайдера */
    z-index: 300;
}

(function($) {
    // получаем доступ к элементу слайдера на странице
    var $dragMe = $(".dragme"),
    // к слайдеру
    $container = $(".sl-container"),
    // и картинке слева
    $viewAfter = $(".view-after");
    // используем свойство draggable из библиотеки с интерфейсом, чтобы получить координаты сдвига слайдера
    $dragMe.draggable({
        containment: "parent",
        drag: function() {
            // при перемещении слайдера меняем ширину картинки слева в стилях
            $viewAfter.css({
                width : parseFloat($(this).css('left')) + 5
            });
        }
    });
    // добавляем реакцию на клик по картинке
    $container.on("click", function(event) {
        // получаем координаты клика, чтобы сместить туда слайдер
        var eventLeft = event.pageX - $container.offset().left - 15;
        // плавно сдвигаем слайдер
        animateTo(eventLeft);
    });
    // функция сдвига слайдера при клике, на входе получаем новые координаты границы картинок
    function animateTo(_left) {
        // анимируем сдвиг слайдера
        $dragMe.animate({
            left: _left
        }, 'slow', 'linear');
        // и картинки
        $viewAfter.animate({
            width: _left
        }, 'slow', 'linear');
    }
})(jQuery);

Текст:

Михаил Полянин

Редактор:

Максим Ильяхов

Художник:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Кирилл Климентьев

Соцсети:

Виталий Вебер

Веб-разработка — это новый чёрный
На базе веб-технологий делают всё — от сложного софта до высокобюджетных игр. Изучите технологии и начните карьеру в ИТ. Старт бесплатно. Попробуйте, вдруг вам понравится.
Изучить
Веб-разработка — это новый чёрный
Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
medium