Построение системы координат при помощи HTML и CSS

Я создал динамическую декартову систему координат. Вероятно, она простовата, но мне кажется интересной. Точки она отображает корректно, а еще в ней можно вывести графики разных функций. Несколько интересных особенностей моей системы:

  • Настраиваемость: выбор цветов, размера, диапазона, центра системы и т. д.
  • Отзывчивость: 100% родительской ширины по умолчанию, но размеры могут быть как относительными, так и абсолютными.
  • Легкость использования: простой перевод функций в CSS.
  • Легковесность: 35 строк CSS и строка HTML для каждой отображаемой точки. Никакого JavaScript.

Да, все верно: я не использую JavaScript для вычислений. Все делается при помощи CSS-метода calc().

Вступление

Однажды в выходной день я проснулся и решил поиграться с HTML и CSS. Заглянув в Twitter, я нашел этот твит:

https://twitter.com/leaverou/status/1441824672199241737

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-переменные как для всего графика (контейнера), так и для каждой точки по отдельности.

Вот несколько переменных для контейнера:

ИмяПо умолчаниюОписание
--c10Число. Размер диаграммы (сколько «ячеек» в целом). Число должно быть положительным.
--cx5Число. Позиция x-оси в диаграмме. Это должно быть целое положительное число, меньшее или равное --c.
--cy5Число. Позиция y-оси в диаграмме. Это должно быть целое положительное число, меньшее или равное --c.
--dsize10Число. Размер точек в пикселях. Число должно быть больше 0.
--dcolor#369Цвет. Цвет точек (в любом формате).
--size100%Длина. Ширина системы координат. Может быть абсолютной или относительной (привязанной к родительскому элементу).

Благодаря CSS-каскаду, мы можем переопределить некоторые из этих значений по отдельности для каждой точки. А еще нам нужно передать значение x для точек:

ИмяПо умолчаниюОписание
--dsize10Число. Размер точек в пикселях. Число должно быть больше 0.
--dcolor#369Цвет. Цвет точек (в любом формате).
--xN/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]

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх