Создание вертикальной шкалы времени на CSS и JavaScript

В этом уроке я расскажу, как создать адаптивную вертикальную шкалу времени с нуля. Во-первых, мы начнем с создания базовой, минимальной структуры и добавим силу CSS псевдо-элементов. Затем, добавим эффекты при прокрутке страницы, для этого нам понадобится JavaScript.

Для начала взгляните на результат, чтобы иметь представление с чем вы будете работать.

See the Pen Building a Vertical Timeline With CSS and a Touch of JavaScript by Envato Tuts+ (@tutsplus) on CodePen.

HTML разметка

Разметка будет очень простая; Обычный неупорядоченный список, внутри каждого элемента списка будет находиться элемент div. Поскольку мы имеем дело с событиями по шкале времени, мы установим внутри каждого контейнера тег time.

Кроме того, для обертки мы воспользуемся тегом section с классом timeline:

<section class="timeline">
  <ul>
    <li>
      <div>
        <time>1934</time>
        Some content here
      </div>
    </li>
     
    <!-- more list items here -->
  </ul>
</section>

Результат выглядит следующим образом:

See the Pen Vertical Timeline Step 1 by Envato Tuts+ (@tutsplus) on CodePen.

Добавление начальных CSS стилей

Сейчас установим несколько основных цветов, определим некоторые структурные CSS правила для элементов списка. Также стилизуем псевдо-элемент ::after для пунктов списка:

.timeline ul li {
  list-style-type: none;
  position: relative;
  width: 6px;
  margin: 0 auto;
  padding-top: 50px;
  background: #fff;
}
 
.timeline ul li::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: inherit;
}

Для наглядности, я убрал контент внутри элементов списка, результат ниже:

See the Pen Vertical Timeline Step 2 by Envato Tuts+ (@tutsplus) on CodePen.

Стилизация элементов временной шкалы

Теперь давайте стилизуем элементы div (я буду называть их "элементы временной шкалы"), которые находятся внутри элементов списка. Опять же, стилизуем псевдо-элемент ::before для этих элементов.

Кроме того, не все элементы временной шкалы имеют одинаковый стиль. Благодаря псевдо-классам :nth-child(odd) и :nth-child(even), мы можем дифференцировать свои стили.

.timeline ul li div {
  position: relative;
  bottom: 0;
  width: 400px;
  padding: 15px;
  background: #F45B69;
}
 
.timeline ul li div::before {
  content: '';
  position: absolute;
  bottom: 7px;
  width: 0;
  height: 0;
  border-style: solid;
}

Отдельный стиль для нечетных элементов:

.timeline ul li:nth-child(odd) div {
  left: 45px;
}
 
.timeline ul li:nth-child(odd) div::before {
  left: -15px;
  border-width: 8px 16px 8px 0;
  border-color: transparent #F45B69 transparent transparent;
}

Отдельный стиль для четных элементов:

.timeline ul li:nth-child(even) div {
  left: -439px;
}
 
.timeline ul li:nth-child(even) div::before {
  right: -15px;
  border-width: 8px 0 8px 16px;
  border-color: transparent transparent transparent #F45B69;
}

Текущий результат:

See the Pen Vertical Timeline Step 3 by Envato Tuts+ (@tutsplus) on CodePen.

Основное различие между "четными" и "нечетными" - это их позиция (left:45px и left: -439px соответственно). Для того чтобы правильно расположить контейнеры, можно выполнить несложное вычисление:

Ширина контейнера + Отступы - Ширина элемента списка = 400px + 45px - 6px = 439px

Добавление интерактивности

Теперь, когда основная структура шкалы времени готова, давайте определим новые условия:

  • По умолчанию элементы временной шкалы (div) должны быть скрыты.
  • Они должны появляться, когда их родитель (li) появляется в окне просмотра.

Первая задача довольно простая. Вторая, немного сложнее. Нам нужно определить, видны ли целевые элементы (li) в текущем окне просмотра, а затем, отобразить дочерний элемент. Для реализации этой функции, мы не будем использовать стороннюю JavaScript библиотеку (такие как WOW.js или ScrollReveal.js), а напишем свой собственный код. К счастью, есть популярная тема на StackOverlow об этой проблеме. Итак, давайте воспользуемся предложенным ответом, для проверки, является ли элемент видимым в текущем окне или нет.

Вот упрощенная функция, которую мы будем использовать:

function isElementInViewport(el) {
  var rect = el.getBoundingClientRect();
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
    rect.right <= (window.innerWidth || document.documentElement.clientWidth)
  );
}

Добавление классов

Далее мы добавим класс in-view когда li появляется в окне просмотра.

Важно соблюдать следующие условия:

  • При загрузке страницы
  • При прокрутке страницы

При необходимости, мы можем провести дополнительные тесты (например, когда меняется размер окна браузера).

Код для нашего примера:

var items = document.querySelectorAll(".timeline li");
 
// код для функции isElementInViewport
 
function callbackFunc() {
  for (var i = 0; i < items.length; i++) {
    if (isElementInViewport(items[i])) {
      items[i].classList.add("in-view");
    }
  }
}
 
window.addEventListener("load", callbackFunc);
window.addEventListener("scroll", callbackFunc);

После добавления JavaScript кода, мы должны увидеть следующий результат:

временная шкала результат

Скрытие и отображение

Давайте вспомним первоначальное требование. По умолчанию все контейнеры должны быть скрыты. Для этого мы используем CSS свойства visibility и opacity. Кроме того, мы используем translate3d(), чтобы переместить их на 200px от своего первоначального положения. До тех пор пока их родительский элемент в поле зрения, мы поменяем отображение и удалим предопределенное смещение. Таким образом, у нас получатся неплохие эффекты.

Еще одна небольшая вещь которую нужно сделать, когда li находится в пределах просмотра, меняем цвет фона у псевдо-элемента ::after.

.timeline ul li::after {
  background: #fff;
  transition: background .5s ease-in-out;
}
 
.timeline ul li.in-view::after {
  background: #F45B69;
}
 
.timeline ul li div {
  visibility: hidden;
  opacity: 0;
  transition: all .5s ease-in-out;
}
 
.timeline ul li:nth-child(odd) div {
  transform: translate3d(200px,0,0);
}
 
.timeline ul li:nth-child(even) div {
  transform: translate3d(-200px,0,0);
}
 
.timeline ul li.in-view div {
  transform: none;
  visibility: visible;
  opacity: 1;
}

Следующая визуализация показывает начальное состояние нашей временной шкалы. Для наглядности я убавил значение непрозрачности элементов, чтобы показать их изначальное расположение:

time line css

И финальное состояние:

css временная шкала

Добавление адаптивности

Мы почти закончили! Последнее, что нам нужно сделать, это сделать наш график адаптивным.

Во-первых, для средних экранов (>600px and ≤900px), мы добавим одну небольшую модификацию. В частности, мы уменьшаем ширину контейнера.

@media screen and (max-width: 900px) {
  .timeline ul li div {
    width: 250px;
  }
  .timeline ul li:nth-child(even) div {
    left: -289px; /*250+45-6*/
  }
}

Выглядит следующим образом:

шкала времени 600

На маленьких экранах (≤600px), все элементы временной шкалы выглядят одинаково; нет различия между "четными" и "нечетными" контейнерами. Внесем небольшие изменения в CSS:

@media screen and (max-width: 600px) {
  .timeline ul li {
    margin-left: 20px;
  }
   
  .timeline ul li div {
    width: calc(100vw - 91px);
  }
   
  .timeline ul li:nth-child(even) div {
    left: 45px;
  }
   
  .timeline ul li:nth-child(even) div::before {
    left: -15px;
    border-width: 8px 16px 8px 0;
    border-color: transparent #F45B69 transparent transparent;
  }
}

На маленьких экранах временная шкала выглядит следующим образом:

css timeline 320

Поддержка браузерами

Демонстрационный пример отлично работает во всех последних версиях браузеров и устройств. Однако на устройствах под управлением iOS, элементы временной шкалы видны всегда.

После тестирования я заметил, что на этих устройствах, свойства window.innerHeight и document.documentElement.clientHeight не возвращают фактическую высоту окна просмотра. В частности, они возвращают большее число. В результате этого несоответствия, все элементы списка получают класс in-view при загрузке страницы.

Хотя это не имеет большого значения (вы можете добавить анимацию только для больших экранов), если вы знаете больше об этой проблеме или вы встречались с ней ранее, не забудьте оставить подробности в комментариях.

Заключение

В этом уроке, мы создали адаптивную вертикальную шкалу времени. Резюмируем весь процесс:

  • Используя простой неупорядоченный список и CSS псевдо-элементы, нам удалось построить основную структуру нашей временной шкалы. Одним из недостатков этого подхода является то, что CSS псевдо-элементы не имеют 100% поддержку всеми браузерами, так что имейте это в виду.
  • Мы воспользовались небольшим фрагментом JavaScript кода, для проверки, находятся ли элементы списка в поле зрения или нет. Затем мы описали собственные CSS стили, чтобы оживить их дочерние элементы. С другой стороны, мы могли бы использовать JavaScript библиотеку.

Я надеюсь, что вам понравился этот урок, и вы будете использовать эту временную шкалу в качестве основы для своих проектов. Если у вас есть какие-либо вопросы, оставляйте их в комментариях ниже!

 

Перевод статьи Building a Vertical Timeline With CSS and a Touch of JavaScript

Вход

Уважаемый пользователь! Мы обнаружили, что вы используете AdBlock и вынуждены скрыть часть материалов на нашем сайте. Siteacademy существует и развивается за счет доходов от рекламы. Просим внести наш сайт в список исключений или отключить Блокировщик рекламы на нашем сайте.