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

2

Від автора: я дуже довго захоплювався дизайном інтерфейсу в аніме Ghost In The Shell, а на цьому тижні зрозумів, що можу відтворити його в SVG. У даній статті я розповім покроково, як я створив такий кругової прогрес-бар.

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

Розмітка

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

0

Трансформація і анімація в CSS і SVG знаходиться поки що на різних рівнях, тому анімацією кілець займається SMIL. Ускладнюємо розмітку, додавши тег animate всередину кожного circle:

0

Одні кільця обертаються за годинниковою стрілкою, інші – проти. У всіх кілець різна швидкість без заданих повторень (тобто нескінченно). Зверніть увагу, що кільця поділені на 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, дотримуючись техніки прогресивного поліпшення інтерфейсів.

Також ви можете змінити швидкість росту сегментів таким чином, щоб усі кільця з’єднувалися одночасно.