Круговой прогресс-бар на CSS

в 9:34, , рубрики: css, css animation, веб-дизайн, Веб-разработка

Круговой прогресс бар на CSS
Подобные индикаторы загрузки можно встретить на многих flash-сайтах. Но можно реализовать такое и с помощью CSS.

HTML

Необходимо три HTML-элемента:

  • spinner — полукруг, который вращается постоянно
  • mask — элемент, скрывающий spinner в течение первой половины анимации
  • filler — элемент, завершающий вторую половину анимации

spinner и filler — две половины нашего круга, поэтому применяем к ним класс .pie

<div class="wrapper">
  <div class="spinner pie"></div>
  <div class="filler pie"></div>
  <div class="mask"></div>
</div>

CSS

Устанавливаем размеры контейнера:

.wrapper {
  width: 250px;
  height: 250px;
  position: relative;
  background: white;
}

CSS для spinner и filler. Ширина каждого равна 50%, т.к. они являются двумя частями одного круга:

.pie {
  width: 50%;
  height: 100%;
  position: absolute;
  background: #08C;
  border: 10px solid rgba(0,0,0,0.4);
}

Spinner должен отображаться как полукруг, кроме этого, с помощью z-index необходимо расположить его поверх filler, но под mask. Еще добавляем анимацию:

.spinner {
  border-radius: 125px 0 0 125px;
  z-index: 200;
  border-right: none;
  animation: rota 10s linear infinite;
}

Filler является второй половиной прогресс-бара, поэтому устанавливаем для анимации steps(1, end) и прозрачность 0:

.filler {
  border-radius: 0 125px 125px 0;
  z-index: 100;
  border-left: none;
  animation: fill 10s steps(1, end) infinite;
  left: 50%;
  opacity: 0;
}

Mask присутствует с начала анимации, поэтому прозрачность равна 1, фон наследуется, а чтобы быть поверх spinner'а, z-index равен 300:

.mask {
  width: 50%;
  height: 100%;
  position: absolute;
  z-index: 300;
  opacity: 1;
  background: inherit;
  animation: mask 10s steps(1, end) infinite;
}

Первая анимация rota — для spinner'а: вращение на 360° за 10 секунд.
Вторая анимация fill — для filler'а: меняется прозрачность с 0 на 1 через 5 секунд.
Вторая анимация mask — для mask'а: меняется прозрачность с 1 на 0 через 5 секунд.

@keyframes rota {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

@keyframes fill {
  0%        { opacity: 0; }
  50%, 100% { opacity: 1; }
}

@keyframes mask {
  0%        { opacity: 1; }
  50%, 100% { opacity: 0; }
}

Анимация по этапам:

  1. spinner находится слева, скрыт маской, filler скрыт
  2. spinner начинает поворачиваться и появляться из-за маски
  3. spinner проходит 72° и продолжает поворачиваться
  4. spinner проходит 108° и продолжает поворачиваться
  5. spinner проходит 144° и продолжает поворачиваться
  6. spinner проходит 180° и продолжает поворачиваться. В этот момент filler становится видимым, а mask исчезает
  7. spinner проходит 216° и продолжает поворачиваться
  8. spinner проходит 252° и продолжает поворачиваться
  9. spinner проходит 288° и продолжает поворачиваться
  10. spinner проходит 324° и продолжает поворачиваться
  11. spinner проходит 360° и возвращается в начальную точку. В этот момент mask становится видимым, а filler исчезает

Бонусы

Пауза при наведении на прогресс-бар:

.wrapper:hover .filler,
.wrapper:hover .spinner,
.wrapper:hover .mask {
  animation-play-state: paused;
}

С помощью z-index можно легко добавить текст, который будет вращаться вместе со spinner'ом:

.spinner:after {
  content: "";
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  top: 10px;
  right: 10px;
  background: #fff;
  border: 1px solid rgba(0,0,0,0.4);
  box-shadow: inset 0 0 3px rgba(0,0,0,0.2);
}

CSS переменные

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

.animation-duration {
  animation-duration: 10s;
}
<div class="wrapper">
  <div class="spinner pie animation-duration"></div>
  <div class="filler pie animation-duration"></div>
  <div class="mask animation-duration"></div>
</div>

Минусы

В подобной реализации анимации есть и минусы:

  • Нет поддержки градиентов.
  • Нет поддержки box-shadow.
  • Неадаптивно. При изменении размеров родительского контейнера необходимо вручную менять border-radius.
  • Несемантично (4 элемента для одной анимации).

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

  • Internet Explorer 10
  • Firefox 12+
  • Chrome
  • Safari 5+
  • Opera 12+
  • iOS Safari 3+
  • Android 2+ (buggy till v4)

Демонстрация

Автор: grokru


  1. Сергей:

    Спасибо конечно за труды но Вы сами пробовали скопировать и вставить то что написали, а потом запустить, скажем в Хроме(я говорю про часть до бонусов)? Зрелище устрашающее. А все потому, что Вы используете border вываливающий за пределы родительских элементов(и не только их, а еще и масок), а еще все эти кубики(spinner, кажется), крутятся вокруг своей оси, а должны крутится по оси своей правой стороны, звучит сложно, поэтому можно просто вставить это transform-origin: 100% 50%;

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js