Формули на сайтах давно перестали бути проблемою. Підключили бібліотеку, написали код у LaTeX, і на сторінці з'явилася охайна математика замість картинок, які ще й розпливалися на екранах з високою щільністю пікселів. Але більшість авторів на цьому зупиняються. А даремно: формулою можна керувати так само, як будь-яким іншим елементом сторінки.

MathJax і зручний редактор формул

MathJax перетворює розмітку LaTeX на повноцінний текст сторінки. Формулу можна виділити, збільшити, її читають екранні програми, вона не залежить від роздільної здатності. У тексті формула записується між розділювачами \( ... \), а як окремий рядок між \[ ... \]. Наприклад, \(E = mc^2\) стоїть у рядку, а формула коренів квадратного рівняння займає власний рядок:

\[x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}\]

Набирати такий код вручну буває незручно, особливо коли формула довга, а дужки й індекси плутаються. Тут допоможе візуальний редактор формул LaTeX. Ви будуєте вираз кнопками та шаблонами, а редактор одразу показує результат і видає готовий код LaTeX, який залишається вставити на сторінку. Інтерфейс українською, тож пошук потрібного символу не забирає багато часу.

Що таке LaTeX і чому MathJax розуміє саме його
LaTeX — це система комп'ютерної верстки, створена для наукових текстів. Її мова запису формул стала фактичним стандартом у математиці та фізиці. MathJax підтримує підмножину цієї мови, тому формула, набрана для статті чи реферату, зазвичай без змін відображається і на сайті.

Підключення MathJax з розширенням html

Для всього, що йдеться далі, знадобиться розширення html. Воно додає команди \class, \style, \cssId та \href. Саме через них ми «чіпляємося» до частин формули. У третій версії MathJax розширення підключають у налаштуваннях:

<script>
window.MathJax = {
 loader: {load: ['[tex]/html']},
 tex: {packages: {'[+]': ['html']}}
};
</script>
<script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>

Налаштування має стояти до скрипта бібліотеки, інакше MathJax його просто не побачить. Це найчастіша причина, чому в новачків «нічого не працює». Усі демонстрації нижче виконуються саме на такому підключенні.

Форматування: колір, стиль, посилання

Анімацію варто будувати на добре засвоєному форматуванні, тому почнемо з нього. Є кілька способів змінити вигляд формули чи її частини:

  • команда \color{red}{...} забарвлює вказану частину виразу;
  • команда \style{...}{...} приймає довільні правила CSS, наприклад товщину шрифту чи фон;
  • команда \class{ім'я}{...} призначає частині формули клас, до якого можна звернутися зі стилів і скриптів;
  • команда \href{адреса}{...} робить частину формули або всю формулу посиланням.

Ось як це виглядає в коді:

\( \color{red}{E} = \color{blue}{m}c^2 \)

\( \style{color:#c0392b; font-weight:bold}{S} = \pi r^2 \)

\( \href{https://qaweb.dev/mathred/math-ua.html}{a^2 + b^2 = c^2} \)

Перша формула має червону енергію та синю масу, у другій площа виділена жирним відтінком, а третя веде на сторінку редактора. Клас, до речі, можна призначити і самій формулі цілком. Це зручно, коли однаково оформлюється кілька виразів. Результат цих трьох записів:

\( \color{red}{E} = \color{blue}{m}c^2 \)

\( \style{color:#c0392b; font-weight:bold}{S} = \pi r^2 \)

\( \href{https://qaweb.dev/mathred/math-ua.html}{a^2 + b^2 = c^2} \)

Чому формула може рухатися

Після обробки MathJax формула перестає бути абстрактним текстом і складається зі звичайних елементів DOM. Усе, що ви позначили через \class, отримує справжній клас у розмітці. А якщо є клас, є й CSS-анімація. Це і весь секрет: ніякої особливої «анімації формул» не існує, ми керуємо звичайними елементами сторінки.

Хороша анімація в навчальному матеріалі не прикрашає, а веде погляд. Рухається лише те, на що зараз слід дивитися.

Приклад перший: пульсація потрібного символу

Найпростіший варіант — змусити один елемент формули плавно змінювати колір. Викладач говорить про число \(\pi\), а воно на екрані підсвічується.

<style>
.pulse {
 display: inline-block;
 animation: pulse 1.6s ease-in-out infinite;
}
@keyframes pulse {
 0%, 100% { color: #2c3e50; }
 50% { color: #e74c3c; }
}
@media (prefers-reduced-motion: reduce) {
 .pulse { animation: none; }
}
</style>

<p>\( S = \class{pulse}{\pi} r^2 \)</p>

Тривалість і кольори підбирайте під фон вашого сайту. Для темної теми, скажімо, світліший відтінок буде помітнішим. Якщо потрібна підсвітка не постійна, а лише при наведенні курсора, замініть анімацію на правило .pulse:hover з властивістю transition. Ось що виходить:

\( S = \class{pulse}{\pi} r^2 \)

Приклад другий: розв'язання, що з'являється поетапно

Для пояснення рівнянь це, мабуть, найкорисніший прийом. Рядки виводу не відкриваються одразу, а виникають один за одним, і учень встигає подумати про кожен крок.

<style>
.step {
 opacity: 0;
 animation: show 0.8s forwards;
}
.s1 { animation-delay: 0.5s; }
.s2 { animation-delay: 1.8s; }
.s3 { animation-delay: 3.1s; }
@keyframes show { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
 .step { opacity: 1; animation: none; }
}
</style>

<div id="solution">
\[
\class{step s1}{ax^2 + bx + c = 0}
\Rightarrow
\class{step s2}{D = b^2 - 4ac}
\Rightarrow
\class{step s3}{x = \frac{-b \pm \sqrt{D}}{2a}}
\]
</div>

<button id="replay">Показати ще раз</button>

<script>
document.getElementById('replay').addEventListener('click', function () {
 document.querySelectorAll('#solution .step').forEach(function (el) {
 el.style.animation = 'none';
 void el.offsetWidth;
 el.style.animation = '';
 });
});
</script>

У \class можна вказати одразу кілька класів через пробіл, тому кожен фрагмент отримує і спільний клас step, і власну затримку. Рядок void el.offsetWidth змушує браузер перерахувати стилі, без нього анімація не перезапуститься. Це старий, але надійний прийом. Анімація стартує під час завантаження сторінки, тож якщо ви її пропустили, натисніть кнопку під формулою:

\[ \class{step s1}{ax^2 + bx + c = 0} \Rightarrow \class{step s2}{D = b^2 - 4ac} \Rightarrow \class{step s3}{x = \frac{-b \pm \sqrt{D}}{2a}} \]

Приклад третій: формула, яку змінює JavaScript

CSS добре працює з виглядом, проте не змінює самого вмісту. Якщо у формулі мають змінюватися числа, наприклад під час демонстрації суми чи залежності від параметра, доведеться перерахувати розмітку та знову викликати MathJax. Розглянемо суму натуральних чисел від одиниці до \(n\), де \(n\) росте на очах у глядача. Над формулою стоїть її код, тож видно, що саме змінюється в записі:

<div id="fx-demo" style="border:1px solid #bbb;border-radius:6px;padding:12px 18px;
margin-bottom:15px;cursor:pointer;">
 <div id="fx-code" style="font-family:monospace;font-size:14px;color:#555;
text-align:center;white-space:nowrap;overflow-x:auto;min-height:22px;"></div>
 <div style="position:relative;height:100px;">
 <div id="fx-a" style="position:absolute;left:0;right:0;top:0;text-align:center;opacity:1;
transition:opacity 0.5s;"></div>
 <div id="fx-b" style="position:absolute;left:0;right:0;top:0;text-align:center;opacity:0;
transition:opacity 0.5s;"></div>
 </div>
 <div id="fx-note" style="display:none;font-size:13px;color:#777;
text-align:center;">Анімацію зупинено. Натисніть на блок, щоб запустити знову.</div>
</div>

<script>
(function () {
 var MAX_STEPS = 40;
 var DELAY = 1500;

 var bs = String.fromCharCode(92);
 var layers = [document.getElementById('fx-a'), document.getElementById('fx-b')];
 var codeBox = document.getElementById('fx-code');
 var demo = document.getElementById('fx-demo');
 var note = document.getElementById('fx-note');
 var n = 0;
 var steps = 0;
 var front = 0;
 var busy = false;
 var timer = null;

 function source(k, total) {
 return bs + 'sum_{i=1}^{' + k + '} i = ' +
 bs + 'frac{' + k + bs + 'cdot ' + (k + 1) + '}{2} = ' +
 bs + 'color{red}{' + total + '}';
 }

 function step() {
 if (busy) { return; }
 busy = true;
 n = n % 10 + 1;
 steps = steps + 1;
 var total = n * (n + 1) / 2;
 var code = source(n, total);
 var back = 1 - front;
 var layer = layers[back];

 if (MathJax.typesetClear) { MathJax.typesetClear([layer]); }
 layer.innerHTML = bs + '[' + code + bs + ']';

 var done = function () {
 layer.style.opacity = '1';
 layers[front].style.opacity = '0';
 front = back;
 codeBox.textContent = code;
 busy = false;
 };

 if (MathJax.typesetPromise) {
 MathJax.typesetPromise([layer]).then(done).catch(function () { busy = false; });
 } else {
 MathJax.Hub.Queue(['Typeset', MathJax.Hub, layer]);
 MathJax.Hub.Queue(done);
 }
 }

 function tick() {
 if (document.hidden) { return; }
 if (steps === MAX_STEPS) {
 clearInterval(timer);
 timer = null;
 note.style.display = 'block';
 return;
 }
 step();
 }

 function run() {
 n = 0;
 steps = 0;
 note.style.display = 'none';
 step();
 timer = setInterval(tick, DELAY);
 }

 demo.addEventListener('click', function () {
 if (timer === null) { run(); }
 });

 function wait() {
 var ready = false;
 if (window.MathJax) {
 if (MathJax.typesetPromise) { ready = true; }
 if (MathJax.Hub) { ready = true; }
 }
 if (ready) {
 setTimeout(run, 800);
 } else {
 setTimeout(wait, 200);
 }
 }
 wait();
})();
</script>

Головна ідея тут у двох шарах, які лежать один на одному. Поки глядач бачить перший шар, новий вираз готується в другому, прихованому: MathJax потрібен час, щоб його намалювати. Щойно малювання завершено, другий шар плавно проявляється, а перший гасне. Читач бачить лише готову формулу, а проміжний «сирий» код і стрибки розмірів зникають. Рядок з кодом оновлюється в момент перемикання, тому він завжди відповідає формулі поруч.

Про межі: число n не росте безкінечно, а після десяти починає відлік заново, тож найбільша сума дорівнює 55. Додатково скрипт рахує кроки і після сорока зупиняється. Це корисно, якщо сторінку залишили відкритою на годину: браузер не перераховує формулу без потреби. Клацання по блоку запускає анімацію спочатку, а поки вкладка неактивна, кроки не виконуються.

Зверніть увагу, що зворотна скісна риска в скрипті складається з коду символу, а не пишеться напряму. Візуальні редактори сайтів нерідко замінюють її спеціальним кодом, і всередині скрипта він уже не розшифровується. З тієї ж причини в скрипті немає знаків &, < і >. Код розрахований на обидві поширені версії бібліотеки: у третій формулу перетворює typesetPromise, у другій для цього є черга MathJax.Hub.Queue. Тепер демонстрація:

\sum_{i=1}^{10} i = \frac{10\cdot 11}{2} = \color{red}{55}
\[ \sum_{i=1}^{10} i = \frac{10\cdot 11}{2} = \color{red}{55} \]

Кілька порад, щоб не зашкодити читачеві

Анімація — приправа, а не основна страва. Коли на сторінці рухається все одразу, увага розсіюється, і користь зникає. Тому дотримуйтеся простих правил:

  1. Анімуйте не більше однієї-двох частин формули одночасно.
  2. Не робіть безкінечні ефекти там, де читач має зосередитися на тексті поруч.
  3. Додайте кнопку повтору для поетапних демонстрацій.
  4. Враховуйте налаштування користувача: для тих, хто вимкнув рух в операційній системі, використовуйте медіазапит prefers-reduced-motion і відключайте анімацію.

Щодо швидкості: переоформлення через JavaScript, як у третьому прикладі, обробляє формулу заново, тож для десятків формул одночасно воно важкувате. Для короткого виразу на сторінці — цілком нормально.

Ну і де це застосувати у вас? Інтерактивні конспекти, онлайн-уроки, пояснення до задач, довідники з фізики. Спробуйте взяти одну зі своїх формул, поставити їй клас і подивитися, як вона оживе. А якщо вигадаєте щось цікаве, наприклад підсвічування розрядів у розкладі числа чи рух вектора в координатах, напишіть у коментарях, поділимося ідеями та розберемо їх окремою статтею.

Рубрика «ОНЛАЙН»
2026-10-03 • Перегляди [ 15 ]

Оцінка - 5.0 (1)

 Схожі публікації