

Я создал динамическую декартову систему координат. Вероятно, она простовата, но мне кажется интересной. Точки она отображает корректно, а еще в ней можно вывести графики разных функций. Несколько интересных особенностей моей системы:
- Настраиваемость: выбор цветов, размера, диапазона, центра системы и т. д.
- Отзывчивость: 100% родительской ширины по умолчанию, но размеры могут быть как относительными, так и абсолютными.
- Легкость использования: простой перевод функций в CSS.
- Легковесность: 35 строк CSS и строка HTML для каждой отображаемой точки. Никакого JavaScript.
Да, все верно: я не использую JavaScript для вычислений. Все делается при помощи CSS-метода calc().
Вступление
Однажды в выходной день я проснулся и решил поиграться с HTML и CSS. Заглянув в Twitter, я нашел этот твит:
Lea Verou использует calc() для генерации диапазона значений в соответствии с линейной функцией. Использование calc() в сочетании с пользовательскими свойствами — интересный подход. Я захотел тоже попробовать. Может, создать пример с квадратичной функцией. Шаг за шагом — и демо начало расти.
Сначала я построил базовый график квадратичной функции в декартовой системе координат. Сделал я это на HTML и CSS. Все значения были прописаны жестко, так что код был слишком специфичным. Затем я попытался сделать его более общим, чтобы он подходил для как можно большего числа функций: линейной, квадратичной, кубической и т. д. У меня вышло. Код достаточно хорошо масштабировался для новых операций, при этом демо не стало слишком сложным.
Код
Весь код декартовой системы координат занимает меньше 40 строчек CSS-кода! И это с учетом применения пользовательских свойств, из-за которых код становится более объемным (но при этом и более настраиваемым).
Вот CSS:
.css-ccs { --c: 10; --cx: 5; --cy: 5; --dsize: 10; --dcolor: #369; --size: 100%; position: relative; width: var(--size); height: 0; padding-top: var(--size); box-sizing: border-box; aspect-ratio: 1/1; background-image: linear-gradient(#0000 calc(var(--cy) * 100% / var(--c) - 1px), #0008 0 calc(var(--cy) * 100% / var(--c) + 1px), transparent 0), linear-gradient(to right, #0000 calc(var(--cx) * 100% / var(--c) - 1px), #0008 0 calc(var(--cx) * 100% / var(--c) + 1px), transparent 0), repeating-linear-gradient(#0002 0 0.5px, #0000 0 calc(100% / var(--c) - 0.5px), #0002 0 calc(100% / var(--c))), repeating-linear-gradient(to right, #0002 0 0.5px, #0000 0 calc(100% / var(--c) - 0.5px), #0002 0 calc(100% / var(--c))); } .css-ccs.no-overflow { overflow: hidden; } .css-css .dot { --translationUnit: 100% / var(--c); --translationX: var(--translationUnit) * var(--cx); --translationY: var(--translationUnit) * var(--cy); /* Vertical is "flipped" in CSS: higher values go down! Do negative! */ --y: calc(var(--translationY) - var(--translationUnit) * var(--function)); width: calc(var(--dsize) * 1px); height: calc(var(--dsize) * 1px); background: var(--dcolor); border-radius: 50%; position: absolute; transform: translate(-50%, -50%); left: calc(var(--translationX) + var(--x) * var(--translationUnit)); top: var(--y); }
Код .css-ccs
будет генерировать оси и направляющие, а .dot
задает место для последующей функции.
Что касается HTML, нам потребуется элемент с классом css-ccs
(CSS Cartesian Coordinates System). Он будет содержать другие элементы с классом dot, которые будут задавать значение x в style. Что-то типа такого:
<div class="css-ccs"> <div class="dot" style="--x: -3;"></div> <div class="dot" style="--x: 0;"></div> <div class="dot" style="--x: 1;"></div> <div class="dot" style="--x: 2;"></div> <!-- ... --> </div>
В идеале мне бы хотелось иметь примерно такой код:
<figure> <figcaption>Graph Title</figcaption> <output data-x="-3"></output> <output data-x="0"></output> <output data-x="1"></output> <output data-x="2"></output> </figure>
И хотя со временем я изменю теги, не все изменения возможны. data-x
был бы лучшим способом указать значение x
: атрибут data
для части данных вместо «переменной стиля». К сожалению, в настоящее время CSS не поддерживает чтение атрибутов data
и передачу значения пользовательскому свойству. Так что пока будет работать пользовательское свойство.
Наконец, нам нужно подключить функцию к графику. Она будет касаться класса .dot
. Сама функция будет довольно простой: мы определим в dot пользовательское свойство --function
с calc()
. Вот и все.
В чем подвох? В CSS нет возведения в степень (как и многих других операций). Поэтому, чтобы сделать что-то вроде x2
, нам придется умножать x
на x
(т. е. x*x
). У нас есть x
в пользовательском свойстве --x
, поэтому нам нужно сделать следующее:
.my-chart .dot { /** * x^2 = x * x = var(--x) * var(--x) */ --function: calc(var(--x) * var(--x)); }
Несколько многословно, но все равно достаточно просто для чтения и понимания.
Переменные
Мы можем настроить график, передав некоторые CSS-переменные как для всего графика (контейнера), так и для каждой точки по отдельности.
Вот несколько переменных для контейнера:
Имя | По умолчанию | Описание |
---|---|---|
--c | 10 | Число. Размер диаграммы (сколько «ячеек» в целом). Число должно быть положительным. |
--cx | 5 | Число. Позиция x-оси в диаграмме. Это должно быть целое положительное число, меньшее или равное --c . |
--cy | 5 | Число. Позиция y-оси в диаграмме. Это должно быть целое положительное число, меньшее или равное --c . |
--dsize | 10 | Число. Размер точек в пикселях. Число должно быть больше 0. |
--dcolor | #369 | Цвет. Цвет точек (в любом формате). |
--size | 100% | Длина. Ширина системы координат. Может быть абсолютной или относительной (привязанной к родительскому элементу). |
Благодаря CSS-каскаду, мы можем переопределить некоторые из этих значений по отдельности для каждой точки. А еще нам нужно передать значение x
для точек:
Имя | По умолчанию | Описание |
---|---|---|
--dsize | 10 | Число. Размер точек в пикселях. Число должно быть больше 0. |
--dcolor | #369 | Цвет. Цвет точек (в любом формате). |
--x | N/A | Число. Указывается обязательно. Значение x, которое должно быть передано в функцию и представлено в графике. |
Примеры и демо
Во несколько примеров графиков:
Линейная функция: x — 3
#axis_x-3 .dot { /** * f(x) = x - 3 * x = var(--x) */ --function: calc(var(--x) - 3); }
See the Pen CSS-only cartesian coordinates system (demo linear function) by Alvaro Montoro (@alvaromontoro) on CodePen.
Квадратичная функция: x2 — 5
#axis_x2-5 .dot { /** * f(x) = x^2 - 5 * x^2 = var(--x) * var(--x) */ --function: calc(var(--x) * var(--x) - 5); }
See the Pen CSS-only cartesian coordinates system (demo quadratic function) by Alvaro Montoro (@alvaromontoro) on CodePen.
Кубическая функция: 0.4x3 — 5.25x — 4
#axis_04x3_525x_4 .dot { /** * f(x) = 0.4x^3 - 5.25x - 4 * 0.4x^3 = var(--x) * var(--x) * var(--x) * 5.25x = 5.25 * var(--x) */ --function: calc(0.4 * var(--x) * var(--x) * var(--x) - 5.25 * var(--x) - 4); }
See the Pen CSS-only cartesian coordinates system (demo cubic function) by Alvaro Montoro (@alvaromontoro) on CodePen.
Обратная квадратичная функция: 1 / x2
#axis_1_x2 .dot { /** * f(x) = 1 / x^2 * x^2 = var(--x) * var(--x) */ --function: calc(1 / (var(--x) * var(--x))); }
See the Pen CSS-only cartesian coordinates system (demo reciprocal square function) by Alvaro Montoro (@alvaromontoro) on CodePen.
Доступность
График выглядит красиво, но имеет некоторые проблемы по части доступности.
Весь график пустой, поэтому нужно добавить хотя бы aria-label
с описанием контента. Также будет не лишним добавить role
. Этот атрибут со значением «img» или «figure» поможет вспомогательным технологиям правильно озвучить график.
Мы можем сделать и лучше: использовать теги <figure>
/<figcaption>
. Но тогда нам придется поупражняться в CSS, потому что фон будет занимать 100% контейнера и элементы могут перекрыть друг друга.
Что касается точек, сейчас они представлены элементами <div>
. Но более семантические теги лучше бы описали, что собой представляют эти элементы. Например, <output>
, <data>
или <samp>
.
Помимо этого точки не имеют никакой информации, так что было бы неплохо добавить какие-то данные для вывода при наведении или фокусе. Если (когда) мы это сделаем, нужно будет учесть доступность в обоих состояниях, чтобы для пользователей вспомогательных технологий была хорошая альтернатива.
Что дальше
Хотя CSS работает и этот эксперимент хорош, в нем не хватает многих важных функций. Некоторые можно добавить или симулировать при помощи HTML и CSS, но для других потребуется JavaScript. Тем не менее, наша система координат рабочая, легковесная и простая.
Ограничения CSS ограничивают и возможности нашего графика. Список математических функций CSS растет (min, max, clamp и т. д.) и мы можем симулировать некоторые распространенные операции (как показывает Ана Тюдор в своих статьях). Но есть также много вычислений, которые невозможно произвести при помощи CSS.
Код, рассмотренный в этой статье, будет полезен для рисования простых примеров. Он не требует подключения тяжелых библиотек. Наконец, поработать с ним было попросту интересно.
Перевод статьи «Building a coordinate system in CSS and HTML».
[customscript]techrocks_custom_after_post_html[/customscript]
[customscript]techrocks_custom_script[/customscript]