Красиво делимся итогами года
medium

Красиво делимся итогами года

Подводим их как настоящие программисты

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

Красиво делимся итогами года

Что делаем

Собирать всё будем на 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();

Апскиллинг, как говорится
Апскиллинг — это, например, переход с уровня junior на уровень middle, а потом — senior. У «Яндекс Практикума» есть курсы ровно для этого: от алгоритмов и типов данных до модных фреймворков.
Изучить вопрос
Апскиллинг, как говорится Апскиллинг, как говорится Апскиллинг, как говорится Апскиллинг, как говорится
Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Еще по теме
medium