Если вы в последнее время заходили в интернет, то, скорее всего, видели красивую анимацию загрузки, которая заполняет страницу, прежде чем элегантно подгрузится ее содержимое.

Некоторые социальные гиганты, такие как Facebook, даже используют этот подход для улучшения загрузки страниц. Как мы можем добиться такого же результата с помощью одного только простого CSS?

Что мы собираемся сделать?

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

 Анимация загрузки

Лишь с одним CSS можно добиться высокой гибкости, изящности приложения и в то же время простоты. 

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

Просто хотите сниппет?

Пожалуйста: CSS Loading Animation.

Нужно ли мне знать, как анимировать, для прочтения этого руководства?

Нет! Мы подробно рассмотрим, что именно вам нужно сделать. На самом деле анимация здесь относительно проста, так что давайте приступим к делу!

Часть 1: Создаем анимацию загрузки

В этой первой части сосредоточимся на том, чтобы собрать анимацию загрузки в одно целое и увидеть ее на статическом HTML-сайте. Цель состоит в том, чтобы пошагово создать настоящий сниппет. Мы будем использовать здесь только HTML и CSS.

Шаг 1: Создаем некоторый образец контента

Для начала нам понадобится небольшой образец контента страницы. Здесь действительно нет никаких ограничений, вы можете создать его с помощью базового HTML и CSS или добавить в свое приложение Create React!

Для этого руководства я собираюсь использовать HTML и CSS с несколькими примерами контента, которые позволят нам увидеть наш сниппет в действии.

Для начала создайте новый HTML-файл. Заполните его чем-нибудь, что даст возможность поиграться с анимацией. Я воспользуюсь сайтом fillerama со строчками из моего любимого сериала “Футурама”!

Статическая веб-страница HTML-CSS с контентом, взятым с fillerama.io

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

my-css-loading-animation-static
- index.html
- main.css

Отслеживайте через коммит!

Шаг 2. Начинаем с основы класса загрузки

В качестве основы давайте создадим новый CSS-класс. В наш CSS-файл добавим:

.loading {
  background: #eceff1;
}

Теперь, когда у нас есть такой класс, давайте добавим его к нескольким или ко всем элементам на нашей странице. Я добавил его к нескольким абзацам, заголовкам и спискам.

<p class="loading">For example...
Статическая веб-страница HTML-CSS с серым фоном для контента

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

.loading {
  color: transparent;
  background: #eceff1;
}
Статическая веб-страница HTML-CSS с серым фоном и прозрачным цветом для контента

Независимо от того, применяете ли вы класс к элементу списка верхнего уровня (<ol> или <ul>) или к отдельному пункту в списке (<li>), всё выглядит как один большой блок. Если мы добавим небольшой отступ в нижнюю часть для всех элементов списка, то увидим, что они отображаются по-разному:

li {
  margin-bottom: .5em;
}
Разница в стиле между применением к верхнему уровню списка или к пунктам в списке

И вот теперь всё начинает складываться воедино, но пока что выглядит скорее, как поле для заполнения. Так что давайте анимируем то, что у нас есть, для создания эффекта загрузки.

Шаг 3. Применяем к классу загрузки стили и анимируем

Прежде чем действительно анимировать класс, нам нужно, чтобы было, что анимировать. Поэтому давайте добавим к классу .loading градиент:

.loading {
  color: transparent;
  background: linear-gradient(100deg, #eceff1 30%, #f6f7f8 50%, #eceff1 70%);
}

Это означает, что нам нужен линейный градиент с наклоном в 100 градусов, где мы начинаем с #eceff1 и переходим к #f6f7f8 на 30% и обратно к #eceff1 на 70%;

Тонкий градиентный фон, который похож на блик

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

Теперь, когда есть основа для анимации, нам сначала нужно создать правило ключевых кадров:

@keyframes loading {
  0% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0 50%;
  }
}

Это правило меняет положение фона от начальных 100% до 0% по оси X.

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

.loading {
  color: transparent;
  background: linear-gradient(100deg, #eceff1 30%, #f6f7f8 50%, #eceff1 70%);
  animation: loading 1.2s ease-in-out infinite;
}

Строчка animation устанавливает ключевой кадр loading, говорит ему продолжаться в течение 1,2 секунд, устанавливает функцию синхронизации как ease-in-out, чтобы все выглядело плавно, и с помощью infinite указывает повторять это в бесконечном цикле.

Никаких изменений — еще не анимировано

Как вы, возможно, заметили, после сохранения этих параметров ничего по-прежнему не происходит. Причина в том, что мы устанавливаем градиент от одного конца элемента DOM до другого, так что нам некуда двигаться!

Давайте попробуем установить background-size для класса .loading.

.loading {
  color: transparent;
  background: linear-gradient(100deg, #eceff1 30%, #f6f7f8 50%, #eceff1 70%);
  background-size: 400%;
  animation: loading 1.2s ease-in-out infinite;
}

Теперь, поскольку наш фон расширяется за пределы выбранного элемента DOM (эта расширенная часть вам не видна), у него есть некоторое пространство для анимации, и вот она!

Наша анимация загрузки!

Отслеживайте через коммит!

Часть 2: Используем анимацию загрузки в динамическом приложении

Теперь, когда в нашем распоряжении есть анимация загрузки, давайте задействуем ее в базовом примере, где имитируется состояние загрузки.

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

Чтобы показать, как это можно сделать, создадим простое приложение React с Next.js.

Шаг 1. Создаем пример приложения React с Next.js

Перейдите в каталог, где вы хотите создать свой новый проект, и введите в командной строке следующее:

yarn create next-app
# или
npm init next-app

Вам будет предложено выбрать некоторые параметры, в частности имя, определяющее каталог, в котором создается проект, и тип проекта. Я использую my-css-loading-animation-dynamic и “Default Starter App”.

Новый проект Next.js

После установки перейдите в ваш новый каталог и запустите сервер разработки:

cd [каталог]
yarn dev
# или
npm run dev
Запуск сервера разработки с Next.JS

Следом давайте заменим содержимое в файле pages/index.js. Я собираюсь извлечь содержимое страницы из предыдущего примера, но мы создадим его аналогично тому, как если бы ожидали, что оно будет исходить от API. Во-первых, давайте добавим контент (content) в качестве объекта над оператором return:

const content = {
  header: `So, how 'bout them Knicks?`,
  intro: `What are their names? I'm Santa Claus! This opera's as lousy as it is brilliant! Your lyrics lack subtlety. You can't just have your characters announce how they feel. That makes me feel angry! Good news, everyone! I've taught the toaster to feel love!`,
  list: [
    `Yes! In your face, Gandhi!`,
    `So I really am important? How I feel when I'm drunk is correct?`,
    `Who are those horrible orange men?`
  ]
}

Чтобы отобразить этот контент, давайте произведем следующую замену внутри <main>:

<main>
  <h1>{ content.header }</h1>
  <p>{ content.intro }</p>
  <ul>
    { content.list.map((item, i) => {
      return (
        <li key={i}>{ item }</li>
      )
    })}
  </ul>
</main>

А что касается стилей, вы можете скопировать и вставить всё из файла main.css из Части 1 в тэг <style> в нижней части индексной страницы. Это даст нам следующее:

Основной контент страницы с Next.js

С этим мы должны вернуться к точке, аналогичной той, на которой закончили в Части 1. Но сейчас мы еще не используем никакую анимацию загрузки.

Отслеживайте через коммит!

Шаг 2. Подделываем загрузку данных из API

Пример, с которым мы работаем, довольно прост. Вы увидите, что всё будет предварительно сгенерировано статически, но это помогает нам создать реалистичную демонстрацию, с помощью которой можно протестировать анимацию загрузки.

Чтобы подделать состояние загрузки, мы используем useState из React, useEffect и старомодный setTimeout, чтобы предзагрузить некоторый “загружаемый” контент, а после того, как setTimeout завершится, нам нужно заменить его на реальный контент страницы. Тем временем, благодаря отдельному экземпляру useState, мы будем знать, что находимся в состоянии загрузки.

В первую очередь нам нужно импортировать зависимости. В начало файла pages/index.js добавьте:

import { useState, useEffect } from 'react';

Над объектом content давайте добавим некоторое состояние:

const [loadingState, updateLoadingState] = useState(true);
const [contentState, updateContentState] = useState({})

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

<h1>{ contentState.header }</h1>
<p>{ contentState.intro }</p>
<ul>
  { contentState.list.map((item, i) => {
    return (
      <li key={i}>{ item }</li>
    )
  })}
</ul>

После того, как вы сохраните и загрузите страницу в таком виде, сразу же заметите, что мы получаем ошибку, потому что свойство list не существует в нашем contentState. Так что сначала надо исправить это:

{ Array.isArray(contentState.list) && contentState.list.map((item, i) => {
  return (
    <li key={i}>{ item }</li>
  )
})}

Затем давайте добавим setTimeout внутри хука useEffect, чтобы имитировать загрузку наших данных. Добавьте под объектом content следующее:

useEffect(() => {
  setTimeout(() => {
    updateContentState(content);
    updateLoadingState(false)
  }, 2000);
}, [])

Как только вы сохраните это и откроете браузер, то заметите, что в течение двух секунд у вас не отображается никакой контент, а затем он загружается. Так имитируется асинхронная загрузка данных.

Отслеживайте через коммит!

Шаг 3. Добавляем анимацию загрузки

Теперь мы наконец-то можем добавить загрузочную анимацию. Так что используем для этого состояние загрузки, которое мы настроили с помощью useState, и если контент загружается, будем добавлять класс .loading в элементы.

Прежде чем приступить, вместо того, чтобы по отдельности добавлять этот класс к каждому элементу DOM, было бы более разумно осуществлять это с помощью CSS и добавления класса к родительскому элементу. Так мы и сделаем.

Во-первых, обновим класс .loading, чтобы он указывал на наши элементы:

.loading h1,
.loading p,
.loading li {
  color: transparent;
  background: linear-gradient(100deg, #eceff1 30%, #f6f7f8 50%, #eceff1 70%);
  background-size: 400%;
  animation: loading 1.2s ease-in-out infinite;
}

Затем мы можем динамически добавить класс к тэгу <main>:

<main className={loadingState ? 'loading' : ''}>

Примечание: если вы используете Sass, то можете управлять стилями загрузки, расширяя класс .loading на экземпляры, которые хотите использовать, или создавать поля для заполнения и расширять уже их!

И если вы обновите страницу, то заметите, что в течение двух первых секунд это все еще просто пустая страница!

Проблема в том, что когда мы загружаем контент, внутри тэгов у нас нет ничего, чему свойство line-height наших элементов могло бы назначить какую-то высоту:

Никакой высоты, когда нет никакого контента

Но мы можем исправить это! Поскольку класс .loading устанавливает текст прозрачным, мы можем просто добавить слово Loading (“Загружается…”) для каждого фрагмента содержимого страницы:

const [contentState, updateContentState] = useState({
  header: 'Loading',
  intro: 'Loading',
  list: [
    'Loading',
    'Loading',
    'Loading'
  ]
})

Примечание: мы не можем использовать здесь пустое пространство, потому что это само по себе не даст нам высоту при рендеринге DOM.

И как только вы сохраните и перезагрузите страницу, первые две секунды будут отображать состояние загрузки, которое соответствует контенту!

Анимация загрузки HTML & CSS

Отслеживайте через коммит!

Немного дополнительных соображений

Эту технику можно использовать довольно широко. Поскольку это CSS-класс, вы можете легко и просто добавлять его, куда захотите.

Если вы не поклонник установки текста “Загружается…” для состояния загрузки, другой вариант — установить фиксированную высоту. Единственная проблема здесь в том, что такой вариант требует больше обслуживания в плане подгонки CSS для соответствия тому, как будет выглядеть загружаемый контент.

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

Читайте также:


Перевод статьи: Colby Fayock How to Use Pure CSS to Create a Beautiful Loading Animation for your App

Предыдущая статьяВ поисках лучшей среды для Julia: Juno или Jupyter?
Следующая статьяКак украсть секреты разработчиков через Websocket?