Как сделать диаграмму на css

Оцените материал
(0 голосов)

Проблема

Секторные диаграммы — даже в самой простой своей двухцветной форме — всегда славились сложностью создания с помощью веб-технологий, несмотря на широкое распространение и массу вариантов использования: от представления простых статистических данных до индикаторов прогресса и таймеров. Реализации чаще всего означают создание нескольких изображений для разных значений секторной диаграммы во внешнем графическом редакторе или необходимость прибегать к помощи объемных каркасов JavaScript, предназначенных для куда более сложных диаграмм. Хотя это уже и вышло из категории невозможного, простого однострочного решения для данной задачи пока что нет. Однако уже сегодня существует несколько хороших способов достичь нужного результата, обеспечивающих к тому же пригодный для дальнейшей поддержки CSS-код.

 

 

Решение на основе трансформации

Это решение оптимально в терминах разметки: оно требует создания всего лишь одного элемента, а остальное реализуется с помощью псевдоэлементов, трансформаций и градиентов CSS. Начнем с простого элемента:
HTML
<div class="pie"></div>
Пока предположим, что нам требуется секторная диаграмма, отображающая жестко закодированное значение 20%. Мы поработаем над тем, чтобы сделать ее гибкой, чуть позже, а для начала применим стили, превращающие элемент в круг, который будет
служить нашим фоном:
.pie {
width: 100px; height: 100px;
border-radius: 50%;
background: yellowgreen;
}


Наша секторная диаграмма будет зеленой (точнее, цвета  yellowgreen ), а процентное значение на ней будет отображаться цветом  #655 . Первой идеей может быть использование трансформации скашивания для формирования сектора, соответствующего процентному значению, но несколько экспериментов быстро докажут, что решение получается слишком беспорядочным. Вместо этого мы закрасим левую и правую части нашего круга двумя обозначенными выше цветами, а затем с помощью вращающегося
псевдоэлемента откроем только небольшую часть, соответствующую требуемому процентному значению.
Для того чтобы закрасить правую часть круга коричневым цветом, воспользуемся простым линейным градиентом:
background-image:
linear-gradient(to right, transparent 50%, #655 0);
Как демонстрирует рисунок ниже это все, что нам требуется.

Теперь перейдем к стилизации псевдоэлемента, который будет служить маской на нашем круге:
.pie::before {
content: '';
display: block;
margin-left: 50%;
height: 100%;
}
На рисунке видно, где в данный момент находится наш псевдоэлемент относительно элемента, представляющего саму секторную диаграмму.

Пока что с ним не связаны никакие стили, и он ничего не закрывает. Это просто невидимый прямоугольник. Прежде чем приступать к определению стилей, зафиксируем несколько наблюдений:
‰ так как мы хотим, чтобы псевдоэлемент закрывал коричневую часть круга, мы должны определить для него зеленый фон с помощью  background-color: inherit . Это поможет избежать дублирования, ведь нам требуется тот же фоновый цвет, что и у родительского элемента; ‰ мы будем вращать псевдоэлемент вокруг центра круга, который совпадает с серединой левой стороны псевдоэлемента, поэтому нам необходимо установить для него значение свойства  transform-origin , равное  0 50% , или же просто  left;‰ мы не хотим, чтобы псевдоэлемент имел форму прямоугольника, так как прямоугольник вылезает за границу секторной диаграммы. Следовательно, нам необходимо либо установить для .pie значение  overflow: hidden , либо использовать подходящее значение border-radius , чтобы превратить его в полукруг. Собирая все вместе, получаем следующий CSS-код для нашего псевдоэлемента:
.pie::before {
content: '';
display: block;
margin-left: 50%;
height: 100%;
border-radius: 0 100% 100% 0 / 50%;
background-color: inherit;
transform-origin: left;
}
Сейчас наша секторная диаграмма выглядит как на рисунке.

Наша простая секторная диаграмма, отображающая разные процентные значения. Сверху вниз: 10% (36deg или .1turn), 20% (72deg или .2turn), 40% (144deg или .4turn).

И здесь начинается главное веселье! Теперь мы можем вращать наш псевдоэлемент, применяя к нему трансформацию  rotate() . Для  20% , которые мы поставили себе целью нарисовать с самого начала, можно использовать значение  72deg (0,2 × 360 = 72) или просто  .2turn , что намного понятнее и читабельнее. Возможно, вы подумали, что задача решена, но не все так просто. Наша секторная диаграмма прекрасно отображает процентные значения от 0 до 50%, но когда мы пытаемся показать на ней значение 60% (применив вращение  .6turn ), происходит то, что вы видите на рисунке.

Однако не теряйте надежды! Это можно исправить, что мы сейчас и сделаем. Если взглянуть на значения от  50% до  100% как на отдельную проблему, то легко заметить, что для нее можно использовать инвертированную версию предыдущего решения: коричневый псевдоэлемент, поворачивающийся от  0 до  .5turn соответственно. Таким образом, для сектора размером 60% код псевдоэлемента выглядит так:
.pie::before {
content: '';
display: block;
margin-left: 50%;
height: 100%;
border-radius: 0 100% 100% 0 / 50%;
background: #655;
transform-origin: left;
transform: rotate(.1turn);
}
Результат этого действия можно видеть на рисунке.

И так как мы теперь умеем изображать любые процентные значения, мы можем даже анимировать секторную диаграмму между 0% и 100% с использованием средств анимации CSS, создав, таким образом, привлекательный индикатор прогресса:
@keyframes spin {
to { transform: rotate(.5turn); }
}
@keyframes bg {
50% { background: #655; }
}
.pie::before {
content: '';
display: block;
margin-left: 50%;
height: 100%;
border-radius: 0 100% 100% 0 / 50%;
background-color: inherit;
transform-origin: left;
animation: spin 3s linear infinite,
bg 6s step-end infinite;
}

ПОПРОБУЙТЕ САМИ!
http://play.csssecrets.io/pie-animated

Все это хорошо, но как определить стили для нескольких статичных секторных диаграмм с разными процентными значениями, то есть для самого распространенного сценария их использования? В идеальном случае нам хотелось бы иметь возможность напечатать простейший код вроде этого:
HTML
<div class="pie">20%</div>
<div class="pie">60%</div>
…и получить две секторные диаграммы, одна из которых показывает  20% , а вторая —  60% . Сначала мы посмотрим, какие нам нужны строковые стили, а затем напишем короткий сценарий для разбора текстового содержимого и добавления этих строковых стилей. Это обеспечит элегантный код, инкапсуляцию, хорошую сопровождаемость и, что наиболее важно, доступность. Проблема управления процентными значениями секторных диаграмм с помощью строковых стилей заключается в том, что CSS-код, отвечающий за установку процента, связан с псевдоэлементом. Как вы уже знаете, невозможно определять строковые стили на псевдоэлементах, поэтому нам понадобится проявить изобретательность. Решение обнаруживается в одном из самых неожиданных мест. Мы будем использовать анимацию, которую уже представили выше, но поставив ее на паузу. Вместо того чтобы прокручивать ее как нормальную анимацию, мы будем использовать отрицательные значения задержки, чтобы иметь возможность статически прогонять ее до любой желаемой точки и останавливать в получившемся состоянии. Звучит непонятно? Да, отрицательные значения  animation-delay не только допускаются спецификацией, но и чрезвычайно полезны в ситуациях, подобных этой. Отрицательная задержка допустима. Аналогично задержке в 0s, это означает, что анимация запускается немедленно, но автоматически прокручивается вперед на абсолютное значение задержки, как если бы восроизведение началось указанный период времени назад. Таким образом создается впечатление, что анимация воспроизводится не с начала, а с определенной точки после прохождения части пути. Так как наша анимация поставлена на паузу, единственным кадром, который отобразится на экране, будет первый кадр, определяемый нашим отрицательным значением  animation-delay . Процентное значение на секторной диаграмме будет соответствовать доле, которую значение  animation-delay составляет от общей продолжительности анимации. Например, с текущей длительностью анимации, равной  6s , для отображения процентного значения  20% нам потребуется установить для  animation-delay значение  -1.2s . Чтобы упростить вычисления, зададим длительность анимации, равную  100s . Помните только, что поскольку анимация ставится на паузу окончательно и никогда не возобновляется, определяемая нами общая длительность анимации ни на что больше не влияет. Осталась одна только последняя проблема: анимация привязана к псевдоэлементу, а мы хотим задать строковый стиль для элемента . pie . Но так как для <div> не определяется никакой анимации, мы можем установить  animation-delay как строковый стиль для этого элемента, а затем использовать  animation-delay: inherit; на псевдоэлементе. Складывая все вместе, получаем такую разметку для секторных диаграмм, соответствующих значениям  20% и  60% :
HTML
<div class="pie"
style="animation-delay: -20s"></div>
<div class="pie"
style="animation-delay: -60s"></div>
СОВЕТ
Вы можете применять эту технику и в других случаях, когда возникает необходимость использовать значения из диапазона без повторений и сложных вычислений, а также для отладки анимации путем пошагового прогона.
А CSS-код для этой анимации из представленного выше становится таким (за исключением правила .pie , которое не меняется):
@keyframes spin {
to { transform: rotate(.5turn); }
}
@keyframes bg {
50% { background: #655; }
}
.pie::before {
/* [Остальные стили не меняются] */
animation: spin 50s linear infinite,
bg 100s step-end infinite;
animation-play-state: paused;
animation-delay: inherit;
}
На этом этапе мы уже можем преобразовать разметку так, чтобы процентные значения использовались в качестве содержимого, как первоначально и планировалось, а также добавить строковые стили animation-delay с помощью простого сценария:
JS
$$('.pie').forEach(function(pie) {
var p = parseFloat(pie.textContent);
pie.style.animationDelay = '-' + p + 's';
});
Обратите внимание, что текст мы менять не стали, так как он нужен нам для обеспечения доступности и удобства использования. В данный момент наши секторные диаграммы выглядят как на рисунке.


Нам нужно скрыть текст, что можно сделать, не теряя в доступности, посредством установки  color:
transparent ; в этом случае текст все так же можно будет выделить и напечатать. В качестве последнего штриха процентные значения можно выровнять по центру секторных диаграмм, чтобы, когда пользователь выделяет содержимое, они не оказывались в произвольных местах страницы. Для этого необходимо следующее:
‰ преобразовать свойство  height диаграммы в  line-height (или добавить значение  line-height , равное  height , но это бессмысленное дублирование кода, поскольку  line-height все равно установит точно такое же вычисленное значение);
‰ задать размер и позицию псевдоэлемента посредством абсолютного позиционирования, чтобы текст не выталкивался вниз; ‰ добавить  text-align: center; для центрирования текста по горизонтали.
Финальная версия кода выглядит так:
.pie {
position: relative;
width: 100px;
line-height: 100px;
border-radius: 50%;
background: yellowgreen;
background-image:
linear-gradient(to right, transparent 50%, #655 0);
color: transparent;
text-align: center;
}
@keyframes spin {
to { transform: rotate(.5turn); }
}
@keyframes bg {
50% { background: #655; }
}
.pie::before {
content: '';
position: absolute;
top: 0; left: 50%;
width: 50%; height: 100%;
border-radius: 0 100% 100% 0 / 50%;
background-color: inherit;
transform-origin: left;
animation: spin 50s linear infinite,
bg 100s step-end infinite;
animation-play-state: paused;
animation-delay: inherit;
}
ПОПРОБУЙТЕ САМИ!
http://play.csssecrets.io/pie-static

Решение на основе SVG

Формат SVG упрощает решение множества задач, связанных с графиками, и секторные диаграммы не исключение. Однако вместо того чтобы создавать секторные диаграммы с помощью контуров, что потребовало бы сложных вычислений, мы воспользуемся небольшим трюком. Начнем с круга:
SVG
<svg width="100" height="100">
<circle r="30" cx="50" cy="50" />
</svg>
Теперь применим к нему простейшую стилизацию:
circle {
fill: yellowgreen;
stroke: #655;
stroke-width: 30;
}
Наш круг с обводкой показан на рисунке.

Обводка в SVG включает не только свойства  stroke и  stroke-width . Существует множество других, менее известных свойств, позволяющих тонко настраивать представление обводки. Одно из них —  stroke-dasharray,предназначенное для создания пунктирных обводок. Например, мы могли бы использовать его так:
stroke-dasharray: 20 10;
Это означает, что нам нужны штрихи длиной  20 и размер промежутка между ними, равный  10.

Сейчас вы, наверное, задаетесь вопросом, что общего может быть у этой иллюстрации использования обводки в SVG с секторными диаграммами. Понятнее станет, когда мы создадим обводку с нулевой шириной и зазором, равным или превышающим длину окружности нашего круга (С = 2πr, так что в нашем случае С = 2π × 30 ≈ 189):
stroke-dasharray: 0 189;
Как вы, вероятно, знаете, эти свойства CSS также доступны как атрибуты элемента SVG, что может быть более практичным решением, если одна из ваших задач  обеспечение переносимости кода.


Рис. 3.60. Несколько значений stroke-dasharray и соответствующий визуальный результат.
Слева направо: 0 189 40 189 95 189 150 189
Как демонстрирует первый круг на рисунке, такой код полностью убирает любую обводку, и нам остается только зеленый круг. Но самое интересное начинается, когда мы принимаемся увеличивать первое значение: поскольку промежуток так велик, в результате мы получаем не пунктирную обводку, а лишь один из штрихов обводки, который покрывает заданную часть окружности. Вероятно, вы уже догадались, к чему я клоню: если мы уменьшим радиус нашего круга так, чтобы обводка полностью покрывала его, то получится фигура, очень сильно напоминающая секторную диаграмму. Например, на рисунке вы видите результатдля случая, когда радиус круга равен 25, а значение stroke-width — 50, как определяется в следующемфрагменте кода:


SVG
<svg width="100" height="100">
<circle r="25" cx="50" cy="50" />
</svg>
circle {
fill: yellowgreen;
stroke: #655;
stroke-width: 50;
stroke-dasharray: 60 158; /* 2π × 25 ≈ 158 */
}
Превратить это теперь в секторную диаграмму, подобную тем, которые мы создавали с помощью предыдущих решений, довольно просто: нам нужно всего лишь добавить зеленый круг большего радиуса прямо под обводкой и повернуть обводку на 90° против часовой стрелки, чтобы она начиналась наверху посередине. Так как элемент  <svg> — это также элемент HTML, мы можем просто определить для него следующие стили:
svg {
transform: rotate(-90deg);
background: yellowgreen;
border-radius: 50%;
}
Финальный результат показан на рисунке.

С помощью этой техники создать анимацию секторной диаграммы от  0% до  100% еще проще. Нужно всего лишь анимировать средствами CSS свойство  stroke-dasharray от  0 158 до  158 158 :
@keyframes fillup {
to { stroke-dasharray: 158 158; }
}
circle {
fill: yellowgreen;
stroke: #655;
stroke-width: 50;
stroke-dasharray: 0 158;
animation: fillup 5s linear infinite;
}
В качестве дополнительного усовершенствования мы можем задать точный радиус круга, такой, чтобы длина его окружности была равна 100 (вернее, составляла бесконечно близкое к 100 значение). Тогда мы сможем задавать значения  stroke-dasharray в процентах, избегая необходимости выполнять вычисления. Поскольку длина окружности равна 2πr, нам требуется радиус, равный 100/2π ≈ 15,915494309, что для наших целей можно округлить до 16. Также мы зададим габаритные размеры для элемента SVG с помощью атрибута
viewBox вместо атрибутов  width и  height , чтобы его размер автоматически корректировался в зависимости от размеров контейнера. После всех этих модификаций разметка для секторной диаграммы будет выглядеть так:
SVG
<svg viewBox="0 0 32 32">
<circle r="16" cx="16" cy="16" />
</svg>
А CSS-код нам потребуется такой:
svg {
width: 100px; height: 100px;
transform: rotate(-90deg);
background: yellowgreen;
border-radius: 50%;
}
circle {
fill: yellowgreen;
stroke: #655;
stroke-width: 32;
stroke-dasharray: 38 100; /* для 38% */
}
Обратите внимание, как легко теперь поменять процентное значение. Но, конечно, даже после такого упрощения нам не хочется повторять всю эту SVG-разметку для каждой секторной диаграммы. Настало время обратиться за помощью к JavaScript и привнести в решение немного автоматизации. Мы напишем небольшой сценарий, который будет брать простую HTML-разметку, подобную следующей…
HTML
<div class="pie">20%</div>
<div class="pie">60%</div>
…и добавлять строковый SVG внутри каждого элемента  .pie со всеми необходимыми элементами и атрибутами. Кроме того, он будет добавлять элемент <title> для обеспечения доступности, чтобы программы чтения экрана также могли понимать, какое процентное значение отображается на каждой диаграмме. Готовый сценарий будет таким:
JS
$$('.pie').forEach(function(pie) {
var p = parseFloat(pie.textContent);
var NS = "http://www.w3.org/2000/svg";
var svg = document.createElementNS(NS, "svg");
var circle = document.createElementNS(NS, "circle");
var title = document.createElementNS(NS, "title");
circle.setAttribute("r", 16);
circle.setAttribute("cx", 16);
circle.setAttribute("cy", 16);
circle.setAttribute("stroke-dasharray", p + " 100");
svg.setAttribute("viewBox", "0 0 32 32");
14. Простые секторные диаграммы 143
title.textContent = pie.textContent;
pie.textContent = '';
svg.appendChild(title);
svg.appendChild(circle);
pie.appendChild(svg);
});
Вот и всё! Не исключено, что вы считаете, что метод с CSS лучше, потому что код проще и выглядит менее инопланетным. Но у метода с SVG есть определенные преимущества, которые решение на чистом CSS обеспечить не в состоянии: ‰ третий цвет добавить очень просто: всего лишь создайте еще один круг с обводкой и сместите его обводку с помощью  stroke-dashoffset . Или же

БУДУЩЕЕ. СЕКТОРНЫЕ ДИАГРАММЫ
Помните конические градиенты? Здесь их помощь также была бы неоценимой. Все, что нам потребовалось бы для создания секторной диаграммы - это круглый элемент с коническим градиентом, включающим две границы перехода цвета. Например, секторную
диаграмму для значения 40%,  можно было бы определить с помощью такого простого кода:
.pie {
width: 100px; height: 100px;
border-radius: 50%;
background: conic-gradient(#655 40%, yellowgreen 0);
}
Помимо этого, после того как будет повсеместно реализована обновленная функция attr(),  мы сможем контролировать процентное значение с помощью простого атрибута HTML:
background: conic-gradient(#655 attr(data-value %),yellowgreen 0);
Это также невероятно упрощает задачу добавления третьего цвета. Например, для создания секторной диаграммы, аналогичной той, которая отображается наверху этого поля, мы бы могли всего лишь добавить еще две границы перехода цвета:
background: conic-gradient(deeppink 20%, #fb3 0, #fb3 30%, yellowgreen 0);
прибавьте его длину штриха к длине штриха предыдущего круга (находящегося под ним). А как вы представляете себе добавление третьего цвета на секторные диаграммы в первом решении?
‰ не приходится задумываться о проблемах с печатью страницы, так как элементы SVG считаются содержимым и печатаются точно так же, как элементы  <img> . Первое решение зависит от определения фона, и, следовательно, распечатать такую диаграмму невозможно; ‰ мы можем менять цвета с помощью строковых стилей, что означает, что они поддаются легкой настройке посредством сценариев (например, могут учитывать значения, введенные пользователем). Первое решение полагается на псевдоэлементы, которые не поддерживают строковые стили (кроме как через наследование), что не всегда удобно.

ПОПРОБУЙТЕ САМИ!
http://play.csssecrets.io/pie-svg
Десерт на сегодня - видео о маленьком парнишке, который отлично боксирует:

Прочитано 328 раз Последнее изменение Воскресенье, 05 марта 2017 13:57
Другие материалы в этой категории:
Понравилась запись? Подпишитесь на обновления по почте:

Оставить комментарий

Убедитесь, что вы вводите (*) необходимую информацию, где нужно
HTML-коды запрещены

Поиск по сайту

Google+