
Я создал динамическую декартову систему координат. Вероятно, она простовата, но мне кажется интересной. Точки она отображает корректно, а еще в ней можно вывести графики разных функций. Несколько интересных особенностей моей системы:
- Настраиваемость: выбор цветов, размера, диапазона, центра системы и т. д.
- Отзывчивость: 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]


