Круговой прогресс-бар по мотивам аниме Ghost in the shell

Круговой прогресс-бар по мотивам аниме Ghost in the shell

От автора: я очень долго восхищался дизайном интерфейса в аниме Ghost In The Shell, а на этой неделе понял, что могу воссоздать его в SVG. В данной статье я расскажу пошагово, как я создал такой круговой прогресс-бар.

Готовый пример с полным кодом в CodePen демо.

Разметка

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

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink">
  <circle cx="50" cy="50" r="22"></circle>
  <circle cx="50" cy="50" r="24"></circle>
  <circle cx="50" cy="50" r="26"> </circle>
  <circle cx="50" cy="50" r="30"></circle>
  <circle cx="50" cy="50" r="34"></circle>
  <circle cx="50" cy="50" r="34"></circle>
  <text x="49" y="54">0</text>
</svg>

Трансформация и анимация в CSS и SVG находится пока что на разных уровнях, поэтому анимацией колец занимается SMIL. Усложняем разметку, добавив тег animate внутрь каждого circle:

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="gits">
  <circle cx="50" cy="50" r="22">
    <animateTransform attributeName="transform"
      attributeType="XML" type="rotate" from="0 50 50"
          to="360 50 50" dur="10s" repeatCount="indefinite" />
  </circle>
  <circle cx="50" cy="50" r="24">
    <animateTransform attributeName="transform"
      attributeType="XML" type="rotate" from="0 50 50"
          to="360 50 50" dur="8s" repeatCount="indefinite" />
  </circle>
  <circle cx="50" cy="50" r="26">
    <animateTransform attributeName="transform"
      attributeType="XML" type="rotate" from="0 50 50"
          to="-360 50 50" dur="8s" repeatCount="indefinite" />
  </circle>
  <circle cx="50" cy="50" r="30">
    <animateTransform attributeName="transform"
      attributeType="XML" type="rotate" from="0 50 50"
          to="360 50 50" dur="14s" repeatCount="indefinite" />
  </circle>
  <circle cx="50" cy="50" r="34">
    <animateTransform attributeName="transform"
      attributeType="XML" type="rotate" from="0 50 50"
          to="360 50 50" dur="18s" repeatCount="indefinite" />
  </circle>
  <circle cx="50" cy="50" r="34">
    <animateTransform attributeName="transform"
      attributeType="XML" type="rotate" from="0 50 50"
          to="-360 50 50" dur="20s" repeatCount="indefinite" />
  </circle>
  <text x="49" y="54">0</text>
</svg>

Одни кольца крутятся по часовой стрелке, другие – против. У всех колец разная скорость без заданных повторений (т.е. бесконечно). Обратите внимание, что кольца поделены на 4 части.

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

circle { 
  stroke: #000;
  fill: none;
  stroke-width: 4px;
  transition: .2s;
  stroke-dashArray: 0 600;
}
text {
  font-family: Titillium Web, sans-serif;
  font-size: 12px;
  text-anchor: middle;
}

Чтобы кольца соприкасались друг с другом, но не наезжали, нам достаточно воспользоваться свойством stroke-width. Свойство stroke-dashArray задает высоту обводки такой, чтобы ее не было видно. По крайне мере, сначала. Свойство text-anchor помещает текст в центр прогресс-бара, а transition запускает анимацию.

JavaScript

Кольца закрутились. С помощью JavaScript они превращаются в круговые сегменты:

var circles = document.getElementsByTagName("circle"),
progress = document.getElementsByTagName("text")[0];

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

function getRandomInRange(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

Для вычисления длины каждой окружности скрипт использует цикл. Для определения стартового значения stroke-dasharray для каждого круга используется случайное число от 20 до 80, а также длина окружности, которая берется из свойства:

for (var j = 0; j < circles.length; ++j) {
	var radius = parseInt(circles[j].getAttribute('r'), 10);
	circles[j].circumference = 2 * radius * Math.PI;
	circles[j].init = getRandomInRange(20,80);
	circles[j].style.strokeDasharray = circles[j].init + " " + circles[j].circumference;
}

Изначально, в коде использовался цикл for…of, но я узнал, что в Safari данный цикл не проходит по дереву узлов в SVG. Позже я опишу эту проблему. Код ниже формирует все круговые сегменты, а мы видим анимацию.

var i = 0;
var timer = setInterval(function() { 
	progress.textContent = i;
		if (i == 100) {
			clearInterval(timer);
		} else {
  			i++;  
  			for (var j = 0; j < circles.length; ++j) {
       			circles[j].style.strokeDasharray = circles[j].init + i + " " + circles[j].circumference;
 			}
}}, 500)

В тег text по таймеру каждые 500 миллисекунд (полсекунды) записывается значение i. Это же значение используется для увеличения dashArray каждого круга, что медленно заполняет кольца.

Улучшения

Есть несколько способов:

Не все сегменты превращаются в замкнутые кольца по завершению таймера. Первого значения stroke-dashArray хватает на заполнение маленького внутреннего круга, но его не хватает на формирование замкнутого внешнего кольца. Мне нравится мой код, но вы можете изменить скрипт, чтобы в конце все кольца соединялись.

В идеале, весь SVG код должен генерироваться через JS и заменять HTML5 тег progress, придерживаясь техники прогрессивного улучшения интерфейсов.

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

Источник: http://thenewcode.com/

Редакция: Команда webformyself.

Практика HTML5 и CSS3 с нуля до результата!

Получите бесплатный пошаговый видеокурс по основам адаптивной верстки с полного нуля на HTML5 и CSS3

Получить

Метки:

Комментарии Вконтакте:

Комментарии Facebook:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree