Псевдоэлемент
::cue позволяет стилизовать текст субтитров и других текстовых дорожек, отображаемых внутри элемента <video>. Эти дорожки подключаются с помощью тега <track>. С помощью ::cue вы можете менять цвет, размер шрифта, фон и другие свойства субтитров, чтобы сделать их более читаемыми или соответствующими дизайну вашего сайта.Внутри ::cue разрешён ограниченный набор свойств:
• background, background-*
• color
• шрифтовые свойства: font, font-*
• outline, outline-*
• ruby-position
• text-combine-upright
• text-decoration, text-decoration-*
• text-shadow
• visibility
• white-space
Поддержка
::cue(<селектор>) может отличаться: в некоторых браузерах он недоступен. Всегда проверяйте поддержку для нужного браузера.Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍10🔥6
requestAnimationFrame() — почему время выполнения js нельзя привязывать к фиксированной частоте кадров!
Браузер самостоятельно управляет процессом обновления изображения на экране. JavaScript выполняется через event loop и не синхронизирован напрямую с частотой обновления дисплея.
Поэтому анимации, построенные на фиксированных задержках, могут вести себя по-разному на разных устройствах.
Значение
В результате скорость такой анимации зависит от количества вызовов JavaScript, а не от реально прошедшего времени. Для визуальных обновлений используется
Браузер планирует callback перед следующим обновлением изображения. Это позволяет выполнять изменения визуального состояния внутри стандартного цикла рендеринга.
Но сама привязка к кадрам не делает анимацию корректной. Если браузер пропустит несколько кадров из-за нагрузки, изменение состояния будет зависеть от количества вызовов
Для независимости от частоты кадров необходимо использовать timestamp, который передаёт
Теперь положение элемента вычисляется через прошедшее время, а не через количество кадров. Такой подход сохраняет одинаковую скорость анимации независимо от того, работает устройство на 60 Hz, 120 Hz или другой частоте обновления.
При этом
После изменения стиля браузеру необходимо получить актуальное значение layout. Поэтому он может выполнить синхронный перерасчёт перед возвратом значения. В больших интерфейсах такие операции внутри animation loop становятся причиной лишней нагрузки и снижения производительности.
Также необходимо управлять жизненным циклом запущенных анимаций. Если компонент удалён, а callback продолжает создавать новые кадры, приложение продолжает выполнять ненужную работу:
Кроме того,
🔥
📣 Code Ready | #практика
Браузер самостоятельно управляет процессом обновления изображения на экране. JavaScript выполняется через event loop и не синхронизирован напрямую с частотой обновления дисплея.
Поэтому анимации, построенные на фиксированных задержках, могут вести себя по-разному на разных устройствах.
setTimeout() не является механизмом синхронизации с браузерным rendering pipeline:let x = 0;
function animate() {
x += 5;
element.style.transform = `translateX(${x}px)`;
setTimeout(animate, 16);
}
animate();
Значение
16 миллисекунд не означает один кадр. Это только минимальная задержка перед следующим выполнением callback. На дисплее 60 Hz это примерно соответствует одному кадру (~16.67 ms), но на устройствах с частотой 120 Hz, 144 Hz и выше браузер не будет синхронизировать выполнение JavaScript с каждым обновлением экрана.В результате скорость такой анимации зависит от количества вызовов JavaScript, а не от реально прошедшего времени. Для визуальных обновлений используется
requestAnimationFrame():let x = 0;
function animate() {
x += 5;
element.style.transform = `translateX(${x}px)`;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
Браузер планирует callback перед следующим обновлением изображения. Это позволяет выполнять изменения визуального состояния внутри стандартного цикла рендеринга.
Но сама привязка к кадрам не делает анимацию корректной. Если браузер пропустит несколько кадров из-за нагрузки, изменение состояния будет зависеть от количества вызовов
requestAnimationFrame(), а не от реального времени.Для независимости от частоты кадров необходимо использовать timestamp, который передаёт
requestAnimationFrame():let startTime = null;
function animate(timestamp) {
if (startTime === null) {
startTime = timestamp;
}
const elapsed = timestamp - startTime;
const x = elapsed * 0.1;
element.style.transform = `translateX(${x}px)`;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
Теперь положение элемента вычисляется через прошедшее время, а не через количество кадров. Такой подход сохраняет одинаковую скорость анимации независимо от того, работает устройство на 60 Hz, 120 Hz или другой частоте обновления.
При этом
requestAnimationFrame() не отменяет особенности браузерного rendering pipeline. Например, чтение геометрии элемента сразу после изменения стилей может вызвать forced synchronous layout:element.style.width = '500px';
const height = element.offsetHeight;
После изменения стиля браузеру необходимо получить актуальное значение layout. Поэтому он может выполнить синхронный перерасчёт перед возвратом значения. В больших интерфейсах такие операции внутри animation loop становятся причиной лишней нагрузки и снижения производительности.
Также необходимо управлять жизненным циклом запущенных анимаций. Если компонент удалён, а callback продолжает создавать новые кадры, приложение продолжает выполнять ненужную работу:
let frameId;
let running = true;
function animate() {
if (!running) return;
update();
frameId = requestAnimationFrame(animate);
}
frameId = requestAnimationFrame(animate);
// cleanup
running = false;
cancelAnimationFrame(frameId);
Кроме того,
requestAnimationFrame() не делает любую анимацию автоматически дешёвой. Для оптимальной работы браузеру желательно избегать постоянных layout и paint операций. Свойства вроде transform и opacity чаще позволяют обновлять слой через compositor без полного перерасчёта страницы.requestAnimationFrame() — это не просто более плавная альтернатива таймерам. Это механизм синхронизации JavaScript с браузерным циклом обновления изображения, который позволяет выполнять визуальные изменения в правильный момент, рассчитывать анимации через реальное время, учитывать разные частоты обновления экранов и избегать лишних операций при работе с интерфейсом.Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍10🔥7
Тег
<samp> используется для оформления вывода данных, которые показываются пользователю в результате выполнения программы. Содержимое этого элемента воспринимается устройствами как простой текст.Внутри тега <samp> размещается информация, которую пользователь может получить в процессе взаимодействия с программой:
• вывод информации о ходе выполнения программы;
• ошибки, предупреждения и подсказки инструментов разработчика или операционной системы;
• приглашение к вводу данных.
Важно не путать
<samp> с <code>: <code> обозначает фрагмент программного кода, а <samp> — результат, который программа выводит пользователю.Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍8🔥5🤝1
В этой статье:
• Познакомитесь с компактным open-source движком для 2D/3D-игр на C++ и JavaScript;• Разберёте его архитектуру, редактор, UI, физику, анимации и поддержку Windows, Linux, macOS, Android, iOS и Web;• Узнаете, как в движок интегрирован ИИ, способный помогать создавать игровые механики и самостоятельно тестировать результат.🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤7🔥3🤝1
7,2 млн рублей призового фонда и реальные задачи космической отрасли 🚀
В сентябре пройдет серия КосмоХакатонов для студентов, молодых ученых и специалистов.
Участникам предстоит за два дня разработать собственное решение, поработать с экспертами и представить проект жюри.
Участников ждут:
🛰 реальные задачи космической отрасли
👥 команды от 3 до 5 человек
💻 очный и онлайн-форматы
🧑💻 работа с экспертами и трекерами
🏆 финал в Москве
Первый КосмоХакатон стартует уже 4 сентября в Ростове-на-Дону.
Дальше серия продолжится в Красноярске, Нижнем Новгороде, Благовещенске и Санкт-Петербурге, а завершится финалом в Москве.
Участие бесплатное. Присоединиться могут студенты, аспиранты, молодые ученые и специалисты от 18 лет.
🔗 Зарегистрироваться: https://космохакатон.рф
В сентябре пройдет серия КосмоХакатонов для студентов, молодых ученых и специалистов.
Участникам предстоит за два дня разработать собственное решение, поработать с экспертами и представить проект жюри.
Участников ждут:
🛰 реальные задачи космической отрасли
👥 команды от 3 до 5 человек
💻 очный и онлайн-форматы
🧑💻 работа с экспертами и трекерами
🏆 финал в Москве
Первый КосмоХакатон стартует уже 4 сентября в Ростове-на-Дону.
Дальше серия продолжится в Красноярске, Нижнем Новгороде, Благовещенске и Санкт-Петербурге, а завершится финалом в Москве.
Участие бесплатное. Присоединиться могут студенты, аспиранты, молодые ученые и специалисты от 18 лет.
🔗 Зарегистрироваться: https://космохакатон.рф
dataset: работа с data-* без getAttribute() и setAttribute()!
HTML-атрибуты
Свойство
При формировании имени свойства префикс
Это преобразование выполняется самим DOM API, поэтому обращаться к исходному имени атрибута вручную не требуется.
Если соответствующего
Удаление свойства через оператор
Важная особенность
Если приложению нужен другой тип, преобразование следует выполнять явно. Это особенно важно для чисел и boolean-значений, поскольку строка
Один из практических сценариев для
Для такого случая надёжнее найти ближайший подходящий элемент через
Для сложных объектов и полноценного состояния приложения
🔥
📣 Code Ready | #практика
HTML-атрибуты
data-* позволяют хранить данные, непосредственно связанные с конкретным DOM-элементом. Для доступа к таким значениям DOM предоставляет стандартный API dataset.<button
data-user-id="42"
data-action="delete"
>
Удалить
</button>
Свойство
dataset возвращает объект DOMStringMap, через который можно читать значения data-* как свойства JavaScript-объекта.const button = document.querySelector('button');
console.log(button.dataset.userId);
// "42"
console.log(button.dataset.action);
// "delete"При формировании имени свойства префикс
data- удаляется, а дефис перед строчной ASCII-буквой a-z удаляется, при этом следующая буква преобразуется в верхний регистр. Поэтому data-user-role становится dataset.userRole.<div
id="user"
data-user-role="admin"
></div>
Это преобразование выполняется самим DOM API, поэтому обращаться к исходному имени атрибута вручную не требуется.
const user = document.querySelector('#user');
console.log(user.dataset.userRole);
// "admin"dataset поддерживает не только чтение. Присваивание свойства изменяет соответствующий data-* атрибут элемента.button.dataset.action = 'restore';
console.log(button.getAttribute('data-action'));
// "restore"
Если соответствующего
data-* атрибута ещё нет, присваивание через dataset создаст его в DOM.button.dataset.loading = 'true';
console.log(button.hasAttribute('data-loading'));
// true
Удаление свойства через оператор
delete удаляет и соответствующий атрибут элемента.delete button.dataset.loading;
console.log(button.hasAttribute('data-loading'));
// false
Важная особенность
dataset: значения представлены строками. При присваивании числа, boolean и другие значения преобразуются в строковое представление.button.dataset.userId = 42;
button.dataset.active = true;
console.log(button.dataset.userId);
// "42"
console.log(button.dataset.active);
// "true"
console.log(typeof button.dataset.userId);
// "string"
Если приложению нужен другой тип, преобразование следует выполнять явно. Это особенно важно для чисел и boolean-значений, поскольку строка
"false" в JavaScript сама по себе является truthy.const userId = Number(button.dataset.userId);
const isActive = button.dataset.active === 'true';
Один из практических сценариев для
data-* — делегирование событий. При этом использовать event.target.dataset напрямую не всегда корректно: фактической целью события может оказаться вложенный span, svg или другой дочерний элемент.<ul id="users">
<li>
<button data-user-id="42">
<span>Удалить</span>
</button>
</li>
</ul>
Для такого случая надёжнее найти ближайший подходящий элемент через
closest() и дополнительно проверить, что он относится к нужному контейнеру.const list = document.querySelector('#users');
list.addEventListener('click', event => {
const button =
event.target.closest('[data-user-id]');
if (!button || !list.contains(button)) {
return;
}
const userId =
Number(button.dataset.userId);
removeUser(userId);
});dataset хорошо подходит для небольших объёмов метаданных, принадлежащих конкретному DOM-элементу: идентификаторов, режимов, состояний интерфейса и параметров поведения. Для сложных объектов и полноценного состояния приложения
data-* использовать не стоит — DOM в таком случае фактически превращается в хранилище состояния, для которого существуют более подходящие структуры данных.button.dataset.userId = '42';
button.dataset.action = 'delete';
button.dataset.confirm = 'true';
dataset — стандартный DOM API поверх data-*, который делает работу с пользовательскими атрибутами короче и удобнее, но не обеспечивает типизацию их значений: значения dataset представлены строками и при необходимости требуют явного преобразования.Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤7🔥5
В этой шпаргалке собраны основные методы Clipboard API для программной работы с буфером обмена. Здесь показано, как читать и записывать текстовые данные, работать с HTML, изображениями и другими поддерживаемыми MIME-типами, создавать ClipboardItem с несколькими представлениями данных, проверять поддержку форматов и извлекать содержимое нужного типа, а также использовать legacy-механизм копирования в качестве fallback.Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤6🔥5