Эта статья выходит в двадцатых числах декабря — самое время подводить итоги и делиться достижениями. Чтобы сделать это как настоящие программисты, мы создали страницу-конструктор — можно запустить, заполнить свои итоги года и сделать красивый скриншот:
Что делаем
Собирать всё будем на Vue.js — фреймворке для создания веб-приложений и интерфейсов. Мы уже рассказывали о нём и собирали простую визуализацию.
Логика такая:
- собираем каркас страницы в HTML-файле;
- делаем красиво в CSS-стилях;
- задаём нужные параметры в скрипте;
- заливаем это всё на сервер и наслаждаемся результатом.
Так как это Vue.js, а скрипт мы будем подключать отдельным модулем, то локально такая страница может не работать из-за политик безопасности браузера. Если это ваш случай — используйте локальный веб-сервер или ставьте MAMP.
Собираем каркас страницы
У нас будет два больших блока — панель с результатами и раздел для редактирования параметров, причём оба блока будут жить внутри виртуального Vue-приложения. Для наполнения возьмём наш стандартный шаблон страницы:
<!DOCTYPE html>
<html lang="ru" >
<head>
<meta charset="UTF-8">
<title>Итоги года</title>
<!-- подключаем стили -->
<link rel="stylesheet" href="./style.css">
</head>
<body>
</body>
</html>
Теперь добавим Vue-приложение, а остальное разместим внутри него:
<!-- создаём vue-приложение -->
<main class="app" v-scope="app()">
</main>
Первый блок — это панель с результатами. Так как мы не знаем, какой заголовок в итоге поставит туда пользователь, мы используем переменную {{title}} — Vue.js сам подставит вместо неё конкретный текст. То же самое мы сделаем для подписей и процентов. Ещё нам там нужны кругляши с процентами — их мы нарисуем с помощью SVG-графики.
А теперь хитрый момент: так как страница заранее не знает, сколько у нас будет кругляшей с параметрами, мы с помощью Vue.js делаем что-то вроде цикла в HTML-тегах:
<div class="metric" v-for="metric in metrics">
Этот блок переберёт все значения переменной metrics
, возьмёт каждое из них как metric
и каждое обработает по одинаковым правилам.
<!-- блок с результатами -->
<div class="app-display">
<!-- заголовок -->
<h2>{{title}}</h2>
<!-- блок с показателями года -->
<div class="metric-group">
<!-- блок с кругляшами -->
<div class="metric" v-for="metric in metrics">
<!-- рисуем кругляш -->
<div class="circle-chart" :style="{ '--selected-color': `var(--${color(metric.pct)}` }">
<!-- задаём положение круга -->
<svg viewBox="0 0 36 36">
<!-- задаём фон -->
<path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
<!-- и рисуем сам круг -->
<path class="circle" :stroke-dasharray="`${metric.pct}, 100`" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
<!-- добавляем показатели процентов -->
<div class="percentage">{{metric.pct}}</div>
</div>
<!-- и надпись под кругом -->
<div class="label">{{metric.title}}</div>
</div>
</div>
</div>
Теперь сделаем раздел с редактированием параметров — вынесем его в отдельный блок, добавим заголовок и точно так же в виртуальном цикле обработаем поля ввода для каждого параметра. Логика будет такая: то, что мы будем вводить в этих полях, сразу повлияет на подписи и процентные значения кругляшей.
<!-- блок с исходными данными -->
<div class="app-editor">
<h2>Подводим итоги года</h2>
<!-- поле ввода, в котором можно менять общий заголовок показателей -->
<input type="text" v-model="title">
<div class="edit-metrics">
<!-- создаём поля ввода для показателей -->
<div class="edit" v-for="metric in metrics">
<!-- подпись и процент выполнения -->
<input type="text" v-model="metric.title">
<input type="number" v-model="metric.pct">
</div>
</div>
</div>
Настраиваем общие стили
Чтобы страница сразу была готова к приёму данных, настроим стили. Для этого создаём файл style.css, кладём его в ту же папку, что и html-файл.
Сначала сделаем общие настройки — создадим глобальные переменные для цветов, пропишем настройки страницы и добавим настройки для всего Vue-приложения:
/* глобальные переменные */
:root {
--light:#F5F5F5;
--dark:#212121;
--brown:#372F25;
--red:#FD3E3E;
--orange:#FFAA48;
--green:#27C86D;
}
/* будем задавать размеры не контента, а блока, в котором он лежит */
*, *::before, *::after {
box-sizing: border-box;
}
/* настройки всей страницы — отступы, фон и текст */
body {
margin: 0;
background-color: var(--dark);
color: var(--light);
font-family: Helvetica, Arial, sans-serif;
}
/* общие настройки приложения */
.app {
/* выравнивание по центру, включаем сетку и располагаем элементы на всю высоту окна */
text-align: center;
display: grid;
place-items: center;
min-height: 100vh;
}
У нас по-прежнему нет контента, поэтому непонятно, что и как настраивать дальше. Чтобы это исправить, подключим скрипт.
Пишем скрипт
Так как это приложение на Vue.js, то мы подключим скрипт на странице не как обычно, напрямую, а как модуль — добавим в конец HTML-файла такую строчку:
<!-- подключаем скрипт как модуль -->
<script type="module" src="./script.js"></script>
Мы такое уже делали в игре Wordle — там в отдельном модуле у нас хранились слова. Теперь создаём файл script.js и заполняем его простым кодом — мы прокомментировали каждую строку, чтобы было легче понять, что там происходит. Обратите внимание на формирование цвета — в зависимости от процентного значения параметр получает свой цвет. При этом цвета здесь возвращаются текстом, а сами значения цветов мы прописали до этого в стилях:
// подключаем vue.js, чтобы создать новое приложение
import {createApp} from "https://cdn.skypack.dev/petite-vue@0.4.1";
// основная переменная-приложение
const app = () => {
// описываем показатели и их значения
const metrics = [
{
title:"Задачи",
pct:91,
},
{
title:"Силы",
pct:15,
},
{
title:"Личные проекты",
pct:52,
},
{
title:"Отдых",
pct:30,
}
]
return {
// ставим предварительный заголовок
title:"Мой 2022 в процентах",
// формируем кругляши с их значениями
metrics,
// и устанавливаем их цвет
color(pct) {
// если большой показатель — то зелёный
if( pct > 89 ) {
return 'green';
// если просто больше половины — то оранжевый
} else if( pct > 49 ) {
return 'orange';
}
// иначе возвращаем красный
return 'red';
}
}
}
// создаём приложение
createApp({app}).mount();
Теперь стало гораздо проще понять, что происходит на странице и что делать дальше со стилями. Возвращаемся к файлу style.css.
Делаем красиво
Когда круги стоят друг под другом, это выглядит так себе. Расположим их в ряд и добавим красивые подписи:
/* включаем гибкую вёрстку в контейнерах */
.metric-group {
display: flex;
}
/* общие настройки каждого кругляша с результатами */
.metric {
/* отступ */
margin: 10px;
/* максимальная высота */
max-width: 90px;
/* используем сетку */
display: grid;
gap: 16px;
/* располагаем всё по центру */
place-items: center;
}
/* настройки подписей */
.metric .label {
/* задаём размер шрифта */
font-weight: 700;
font-size: 0.9rem;
}
Теперь займёмся оформлением кругов — сделаем их побольше, добавим красок в зависимости от значения процентов и настроим внешнюю обводку:
/* настройки круговой диаграммы */
.circle-chart {
/* используем выбранный цвет */
--selected-color: var(--green);
color: var(--selected-color);
/* ширина диаграммы */
width: 75px;
/* располагаем элементы в сетке по центру */
display: grid;
place-items: center;
}
/* настраиваем порядок вывода результатов */
.circle-chart > * {
grid-column: 1/-1;
grid-row: 1/-1;
}
/* настраиваем фон кругляша */
.circle-chart .circle-bg {
fill: var(--brown);
stroke: var(--brown);
/* толщина границы */
stroke-width: 2.8;
}
Почти то, что нужно, но не хватает внешней обводки, которая совпадала бы с количеством процентов внутри параметра. Чтобы сделать такое, добавим в кругляши свой фон и покажем им, как рисовать внешнюю обводку тем же цветом, что и значение процентов:
/* настраиваем анимацию заполнения кругляша */
.circle-chart .circle {
fill: none;
/* используем выбранный цвет */
stroke: var(--selected-color);
/* толщина обводки */
stroke-width: 2.8;
stroke-linecap: round;
/* вид и направление анимации */
animation: progress 1.2s ease-in-out forwards;
}
/* настройки внутреннего текста с процентами */
.circle-chart .percentage {
/* устанавливаем шрифт и его размер */
font-family: Monaco, monospace;
font-weight: bold;
font-size: 1.4rem;
}
Настраиваем анимацию и внешний вид полей ввода
Наверху уже почти всё готово, осталось настроить внешний вид раздела с полями ввода. Заодно добавим анимацию отрисовки внешней обводки у кругляшей:
/* настройки блока редактирования параметров */
.app-editor {
/* выравниваем элементы по вертикальной оси */
align-self: end;
/* отступы между ними */
margin-bottom: 50px;
}
/* раскадровка анимации */
@keyframes progress {
0% {
/* просто рисуем обводку кругляша */
stroke-dasharray: 0 100;
}
}
/* настройки полей ввода */
input {
/* размер шрифта */
font-size: 1.2rem;
font-weight: 700;
/* внутренний отступ */
padding: 12px;
/* используем глобальные цвета для фона и цвета текста */
background-color: var(--brown);
color: var(--light);
/* убираем границу */
border: none;
/* внешний отступ */
margin: 5px 2px;
}
Теперь всё готово: можно менять названия параметров и их процентные значения — все изменения тут же появятся на странице без перезагрузки. Таким же образом можно нарисовать интерфейс настоящего веб-приложения или использовать это для вывода результатов аналитики в реальном времени.
Посмотреть работу и подвести итоги года на странице проекта.
<!DOCTYPE html>
<html lang="ru" >
<head>
<meta charset="UTF-8">
<title>Итоги года</title>
<!-- подключаем стили -->
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- создаём vue-приложение -->
<main class="app" v-scope="app()">
<!-- блок с результатами -->
<div class="app-display">
<!-- заголовок -->
<h2>{{title}}</h2>
<!-- блок с показателями года -->
<div class="metric-group">
<!-- блок с кругляшами -->
<div class="metric" v-for="metric in metrics">
<!-- рисуем кругляш -->
<div class="circle-chart" :style="{ '--selected-color': `var(--${color(metric.pct)}` }">
<!-- задаём положение круга -->
<svg viewBox="0 0 36 36">
<!-- задаём фон -->
<path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
<!-- и рисуем сам круг -->
<path class="circle" :stroke-dasharray="`${metric.pct}, 100`" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
<!-- добавляем показатели процентов -->
<div class="percentage">{{metric.pct}}</div>
</div>
<!-- и надпись под кругом -->
<div class="label">{{metric.title}}</div>
</div>
</div>
</div>
<!-- блок с исходными данными -->
<div class="app-editor">
<h2>Подводим итоги года</h2>
<!-- поле ввода, в котором можно менять общий заголовок показателей -->
<input type="text" v-model="title">
<div class="edit-metrics">
<!-- создаём поля ввода для показателей -->
<div class="edit" v-for="metric in metrics">
<!-- подпись и процент выполнения -->
<input type="text" v-model="metric.title">
<input type="number" v-model="metric.pct">
</div>
</div>
</div>
</main>
<!-- подключаем скрипт как модуль -->
<script type="module" src="./script.js"></script>
</body>
</html>
/* глобальные переменные */
:root {
--light:#F5F5F5;
--dark:#212121;
--brown:#372F25;
--red:#FD3E3E;
--orange:#FFAA48;
--green:#27C86D;
}
/* будем задавать размеры не контента, а блока, в котором он лежит */
*, *::before, *::after {
box-sizing: border-box;
}
/* настройки всей страницы — отступы, фон и текст */
body {
margin: 0;
background-color: var(--dark);
color: var(--light);
font-family: Helvetica, Arial, sans-serif;
}
/* общие настройки приложения */
.app {
/* выравнивание по центру, включаем сетку и располагаем элементы на всю высоту окна */
text-align: center;
display: grid;
place-items: center;
min-height: 100vh;
}
/* включаем гибкую вёрстку в контейнерах */
.metric-group {
display: flex;
}
/* общие настройки каждого кругляша с результатами */
.metric {
/* отступ */
margin: 10px;
/* максимальная высота */
max-width: 90px;
/* используем сетку */
display: grid;
gap: 16px;
/* располагаем всё по центру */
place-items: center;
}
/* настройки подписей */
.metric .label {
/* задаём размер шрифта */
font-weight: 700;
font-size: 0.9rem;
}
/* настройки круговой диаграммы */
.circle-chart {
/* используем выбранный цвет */
--selected-color: var(--green);
color: var(--selected-color);
/* ширина диаграммы */
width: 75px;
/* располагаем элементы в сетке по центру */
display: grid;
place-items: center;
}
/* настраиваем порядок вывода результатов */
.circle-chart > * {
grid-column: 1/-1;
grid-row: 1/-1;
}
/* настраиваем фон кругляша */
.circle-chart .circle-bg {
fill: var(--brown);
stroke: var(--brown);
/* толщина границы */
stroke-width: 2.8;
}
/* настраиваем анимацию заполнения кругляша */
.circle-chart .circle {
fill: none;
/* используем выбранный цвет */
stroke: var(--selected-color);
/* толщина обводки */
stroke-width: 2.8;
stroke-linecap: round;
/* вид и направление анимации */
animation: progress 1.2s ease-in-out forwards;
}
/* настройки внутреннего текста с процентами */
.circle-chart .percentage {
/* устанавливаем шрифт и его размер */
font-family: Monaco, monospace;
font-weight: bold;
font-size: 1.4rem;
}
/* настройки блока редактирования параметров */
.app-editor {
/* выравниваем элементы по вертикальной оси */
align-self: end;
/* отступы между ними */
margin-bottom: 50px;
}
/* раскадровка анимации */
@keyframes progress {
0% {
/* просто рисуем обводку кругляша */
stroke-dasharray: 0 100;
}
}
/* настройки полей ввода */
input {
/* размер шрифта */
font-size: 1.2rem;
font-weight: 700;
/* внутренний отступ */
padding: 12px;
/* используем глобальные цвета для фона и цвета текста */
background-color: var(--brown);
color: var(--light);
/* убираем границу */
border: none;
/* внешний отступ */
margin: 5px 2px;
}
// подключаем vue.js, чтобы создать новое приложение
import {createApp} from "https://cdn.skypack.dev/petite-vue@0.4.1";
// основная переменная-приложение
const app = () => {
// описываем показатели и их значения
const metrics = [
{
title:"Задачи",
pct:91,
},
{
title:"Силы",
pct:15,
},
{
title:"Личные проекты",
pct:52,
},
{
title:"Отдых",
pct:30,
}
]
return {
// ставим предварительный заголовок
title:"Мой 2022 в процентах",
// формируем кругляши с их значениями
metrics,
// и устанавливаем их цвет
color(pct) {
// если большой показатель — то зелёный
if( pct > 89 ) {
return 'green';
// если просто больше половины — то оранжевый
} else if( pct > 49 ) {
return 'orange';
}
// иначе возвращаем красный
return 'red';
}
}
}
// создаём приложение
createApp({app}).mount();