Перейти к содержимому
Поиск
Разработка

NoSleep.js: как не дать экрану погаснуть — примеры Wake Lock

Разбираем NoSleep.js и Screen Wake Lock API: когда удержание экрана полезно, как включать и останавливать режим, замечать системное освобождение и проверять результат. Интерактивная мастерская, пять примеров JavaScript и исходники.

Александр Некрасов
NoSleep.js: явное включение удержания экрана, таймер и кнопка остановки

Открыли инструкцию на телефоне, положили его рядом с рабочим местом — и через минуту экран погас. Приходится разблокировать устройство, искать нужный шаг и возвращаться к делу. NoSleep.js помогает добавить на сайт режим «Оставить экран включённым». Разберём, как он устроен, когда нужен и почему одной команды включения недостаточно для удобного интерфейса.

В статье — работающий стенд, пять примеров JavaScript, сравнение с нативным Screen Wake Lock API и порядок проверки на настоящем телефоне. Начните с мастерской, а затем выберите подход для своего проекта.

Авторская схема управления: кнопка включения на ограниченное время, понятный статус запроса и простое отключение.
Авторская схемаКак выглядит понятное управление активностью экрана

Хороший интерфейс объясняет, что включается, показывает реальное состояние и позволяет закончить режим. Иллюстрация показывает принцип нашей демонстрации; это не официальный интерфейс NoSleep.js.

Увеличить изображение ⤢

Когда удержание экрана помогает пользователю

Постоянно включённый экран нужен в конкретном сценарии. Например, человек читает пошаговую инструкцию, показывает QR-код другому устройству или наблюдает за показателями панели. В такие моменты касаться дисплея только ради его подсветки неудобно. Screen Wake Lock API предназначен для запроса удержания экрана у видимой страницы; описание механизма есть в документации MDN.

Инструкция рядом с работой

Пользователь нажимает «Начать», проходит шаги и выключает режим после завершения. Кнопка остановки остаётся рядом с текущим шагом.

Показ QR-кода

Режим включается на время показа кода. После закрытия окна приложение освобождает запрос, а не оставляет подсветку без срока.

Панель наблюдения

Пользователь включает режим для просмотра показателей. Само обновление данных настраивается отдельно и не становится надёжнее от удержания экрана.

Для практики можно взять наш генератор QR-кодов и представить режим показа готового кода. Для панели подойдёт страница из конструктора Bootstrap 5. Встраивайте функцию вокруг законченного действия: «показываю», «читаю», «наблюдаю». Название «Не гасить экран» само по себе ещё не объясняет, когда режим закончится.

Как работает NoSleep.js: нативный запрос и запасной видеорежим

NoSleep.js — небольшая библиотека Rich Tibbett с методами enable() и disable(). В проверенном package.json указана версия 0.12.0. Перед обновлением зависимости сверяйте её поведение с кодом своей версии.

В исходнике NoSleep.js сначала проверяется navigator.wakeLock. При его наличии библиотека запрашивает screen. Если API отсутствует, обычно используется встроенный видеоэлемент. Отдельная ветвь для iOS ниже 10 содержит старый приём с таймером и изменением адреса страницы; её не стоит переносить в новый интерфейс.

Отказ нативного запроса не переводит библиотеку автоматически в видеорежим. После системного освобождения запроса обработчик пишет сообщение в консоль, но не сбрасывает enabled. Поэтому isEnabled — флаг библиотеки, а не надёжный датчик текущего удержания. При возврате видимости библиотека может повторять запрос самостоятельно; в нашей мастерской повтор запускается кнопкой.

Важная граница. Удержание экрана не даёт странице постоянную работу в фоне и не отменяет ручную блокировку телефона. Видеорежим означает запуск обходного механизма; его результат проверяют на конкретном устройстве. Поддержку нельзя обещать для любого браузера, встроенного WebView и режима энергосбережения.
Как выбрать механизм для своего интерфейса
Подход Когда рассмотреть Что контролировать
Screen Wake Lock API Новый проект с поддерживаемыми браузерами; важен точный статус запроса. Поддержку, отказ, объект запроса, событие освобождения и завершение сценария.
NoSleep.js Нужна библиотечная обёртка и исследование запасного видеорежима. Выбранный механизм, ограничения конкретной версии и реальный результат на телефоне.
Обычный режим страницы Удержание не требуется или устройство отказало. Сохранить полезность страницы: инструкцию можно читать, данные — просматривать.

Мастерская: включите режим и проверьте его поведение

Выберите NoSleep.js: автоматически или Только Wake Lock API. Затем задайте срок — 2, 5 или 10 минут — и нажмите кнопку включения. Само открытие страницы не удерживает экран. «Автоматически» здесь означает выбор механизма библиотекой, а не запуск без вашего действия.

Явное включение · Понятный статус

Проверьте удержание экрана

Запустите NoSleep.js или системный Wake Lock, проверьте состояние и остановите режим. Удержание включается только по вашей кнопке; при уходе со страницы стенд отключает его.

Открыть стенд отдельно ↗

Если сайт запрещает Wake Lock внутри встроенного окна, используйте отдельный стенд. Проверять фактическое отключение дисплея нужно на своём устройстве.

Повторите в своём проекте

Исходники стенда, локальные зависимости с лицензиями и инструкция запуска. Без сторонних API.

Скачать пример проекта
  1. Посмотрите возможности браузера. Наличие API объясняет доступный путь, но ещё не означает выданного разрешения.
  2. Запустите выбранный режим. Прочитайте статус. Для нативного запроса он показывает результат API; для видеорежима — запуск видео.
  3. Переключитесь на другую вкладку. Стенд остановит режим. После возвращения включите его заново вручную.
  4. Завершите проверку. Нажмите остановку или дождитесь выбранного срока. Убедитесь, что интерфейс сообщил о завершении.

Короткий журнал помогает увидеть порядок действий: запрос отправлен, режим запущен, вкладка скрыта, режим остановлен. Это полезнее одной зелёной лампочки. Сообщение о запуске видео не подтверждает физическое удержание дисплея: для этого ниже есть отдельный сценарий проверки на телефоне.

В архиве исходников — наша оболочка мастерской и используемые зависимости с лицензиями. Её можно открыть как самостоятельную страницу, изменить подписи и встроить в собственный проект. Демонстрация не отправляет содержимое формы на сторонний сервис; запрос удержания обрабатывается браузером устройства.

Подключение: проверка возможностей и первый запуск

Сначала решите, какой результат хотите сообщать посетителю. «API доступен» — проверка возможности. «Запрос выдан» — результат обращения к браузеру. «Экран действительно не погас» — наблюдение на телефоне. Эти три утверждения относятся к разным этапам.

Для нативного варианта проверьте безопасный контекст и наличие API. На опубликованном сайте используйте HTTPS. Такое ограничение описано в условиях Screen Wake Lock API.

const status = document.querySelector('#status');
const nativeAvailable = window.isSecureContext &&
  typeof navigator.wakeLock?.request === 'function';

status.textContent = nativeAvailable
  ? 'Можно запросить Wake Lock API'
  : 'Нативный запрос здесь недоступен';

NoSleep.js устанавливается командой npm install nosleep.js. Импорт ниже подходит для проекта со сборщиком, например Vite. Для обычной HTML-страницы можно подключить файл из dist и использовать глобальный NoSleep; оба варианта описаны в репозитории библиотеки. Не помещайте bare-импорт 'nosleep.js' в HTML без сборки или настроенной карты импортов.

Минимальный пример использует кнопки #start, #stop и текстовый блок #status. Кнопка остановки доступна после завершения запроса. Статус намеренно говорит о запуске механизма: это короткая демонстрация API библиотеки, без наблюдения за последующим системным освобождением.

import NoSleep from 'nosleep.js';

const noSleep = new NoSleep();
const start = document.querySelector('#start');
const stop = document.querySelector('#stop');
const status = document.querySelector('#status');
stop.disabled = true;

start.addEventListener('click', async () => {
  start.disabled = true;
  try {
    await noSleep.enable();
    status.textContent = 'Механизм NoSleep запущен';
    stop.disabled = false;
  } catch (error) {
    status.textContent = `Не удалось включить: ${error.name}`;
    start.disabled = false;
  }
});

stop.addEventListener('click', () => {
  noSleep.disable();
  stop.disabled = true;
  start.disabled = false;
  status.textContent = 'Остановлено';
});

Вызывайте enable() непосредственно в обработчике явного действия пользователя. Не ставьте перед ним загрузку данных или другую длительную асинхронную операцию. В полноценной реализации добавьте отмену незавершённого запроса и остановку при скрытии страницы; такая обработка есть в скачиваемой мастерской.

Нативный пример: сохраняем запрос и замечаем освобождение

Если запасной видеорежим не нужен, можно обращаться к API напрямую. request('screen') возвращает объект WakeLockSentinel. Запрос может быть отклонён, в том числе из-за невидимой страницы, политики разрешений или состояния устройства. Точные условия собраны в описании request().

Следующий пример рассчитан на элементы #start, #stop и #status. Счётчик revision отличает текущую попытку от отменённой: если пользователь нажал остановку, пока ответ ещё идёт, поздно выданный запрос будет сразу освобождён.

let sentinel = null;
let revision = 0;
const status = document.querySelector('#status');

async function startScreen() {
  const current = ++revision;
  try {
    if (!window.isSecureContext || !navigator.wakeLock?.request) {
      throw new Error('Wake Lock API недоступен');
    }
    if (sentinel && !sentinel.released) return;
    const acquired = await navigator.wakeLock.request('screen');
    if (current !== revision || document.hidden) {
      await acquired.release();
      return;
    }
    sentinel = acquired;
    acquired.addEventListener('release', () => {
      if (sentinel !== acquired) return;
      sentinel = null;
      status.textContent = 'Запрос освобождён';
    }, { once: true });
    status.textContent = acquired.released
      ? 'Запрос уже освобождён' : 'Запрос удержания получен';
  } catch (error) {
    if (current === revision) status.textContent = error.message;
  }
}

async function stopScreen() {
  const current = ++revision;
  const previous = sentinel;
  sentinel = null;
  if (previous && !previous.released) await previous.release();
  if (current === revision) status.textContent = 'Остановлено';
}

document.querySelector('#start').addEventListener('click', startScreen);
document.querySelector('#stop').addEventListener('click', stopScreen);

Событие release помогает обновить интерфейс независимо от причины освобождения. Метод release() возвращает Promise; после завершения сценария запрос нужно освободить. См. описание release(). В рабочем проекте также ограничьте повторные нажатия на запуск, пока текущая попытка обрабатывается.

Скрытая вкладка, завершение задачи и повторный запуск

Шесть шагов: видимая страница, явное нажатие, запрос браузеру, показ результата, снятие удержания при уходе или по таймеру, ручное повторное включение.
Авторская схемаЖизненный цикл удержания экрана

В нашей демонстрации удержание снимается при скрытии страницы, по таймеру или кнопке. После возвращения его нужно включить заново. Браузер может самостоятельно отказать в запросе или снять удержание — это должно отражаться в статусе.

Увеличить изображение ⤢

Удобный интерфейс имеет понятную последовательность: ожидание, запрос, активный режим, остановка или ошибка. Переход на другую вкладку — отдельное событие. visibilitychange сообщает об изменении видимости, а новое состояние читается из document.visibilityState. Это описано в документации события.

Для примера выше добавьте остановку при скрытии и уходе со страницы. При возвращении оставьте кнопку повторного запуска. Пользователь мог отвлечься надолго или уже закончить задачу; ручное возобновление понятнее неожиданного включения.

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') void stopScreen();
});
window.addEventListener('pagehide', () => {
  void stopScreen();
});

В режиме NoSleep недостаточно ориентироваться только на isEnabled. В собственной интеграции заведите состояния интерфейса и явно вызывайте disable(), когда сценарий заканчивается. Если нужен точный текущий статус нативного запроса, работайте с его объектом и событием освобождения, как в предыдущем примере.

Срок действия стоит показывать рядом с кнопкой. В простом нативном примере обработчик ниже добавляет автоматическую остановку через пять минут. Он дополняет предыдущий код, а не заменяет функцию запуска.

let autoStop = null;
document.querySelector('#start').addEventListener('click', () => {
  window.clearTimeout(autoStop);
  autoStop = window.setTimeout(() => {
    void stopScreen();
  }, 5 * 60 * 1000);
});
document.querySelector('#stop').addEventListener('click', () => {
  window.clearTimeout(autoStop);
});

Таймер этого короткого примера отсчитывается от нажатия. В нашей мастерской отсчёт запускается после успешного ответа и останавливается вместе с режимом. Для собственной панели выберите поведение заранее: десять минут просмотра, время показа QR-кода или конкретный этап инструкции. Не оставляйте срок скрытой настройкой, о которой посетитель узнаёт только после внезапного выключения.

Как оформить функцию, чтобы она была понятной

Дайте действию обычное название: «Не гасить экран» или «Включить на 5 минут». Рядом покажите, что произойдёт при переключении вкладки и как остановить режим. Слово WakeLock можно оставить в технических подробностях. Человек пришёл читать инструкцию, а не выбирать браузерный API.

  • Отдельная кнопка включения. Посетитель осознанно запускает режим; касание произвольной части страницы не становится скрытым согласием.
  • Остановка на видном месте. Не прячьте её только в настройках. На длинной инструкции действие может находиться в закреплённой панели.
  • Статус словами. «Запрашиваем», «Запрос получен», «Остановлено» и «Браузер отказал» различают реальные состояния. Цвет дополняет текст.
  • Клавиатура и читатель экрана. Используйте настоящие кнопки, видимый фокус и role="status" для спокойных сообщений. Не объявляйте каждую секунду отсчёта.
  • Работа без удержания. При отказе оставьте доступ к содержимому. Ошибка этой дополнительной функции не должна блокировать всю страницу.

Свяжите режим с действием в интерфейсе. Закрыли инструкцию — остановили удержание. Закончили показ кода — остановили. Открыли настройки панели — можно сохранить просмотр данных, но объяснить, продолжается ли режим. Такие правила проще поддерживать, когда они записаны до разработки.

Почему не включилось: разбор типичных ситуаций

Диагностика без обещаний, которые сайт не может выполнить
Что видите Что проверить Что показать человеку
Нативный API недоступен Безопасный контекст, поддержку браузера, встроенный просмотрщик. «Этот браузер не поддерживает нативный режим. Страница продолжает работать».
NotAllowedError Видимость страницы, Permissions Policy, условия устройства. По одному имени ошибки точная причина не определяется. «Браузер не разрешил запрос. Оставьте страницу открытой и повторите».
Статус изменился после переключения вкладки Обработку скрытия и системного освобождения. «Режим остановлен. Для продолжения включите его снова».
Видео запустилось, экран всё равно погас Этот телефон, браузер, время ожидания и настройки энергосбережения. «Видеорежим не подтвердил удержание на вашем устройстве».
В отдельной странице работает, во встраивании — нет Заголовок Permissions Policy и разрешение для iframe. Объясните недоступность встроенного режима и дайте ссылку на отдельную страницу.

Для встраиваемых страниц доступ регулируется политикой screen-wake-lock. У стороннего iframe может потребоваться разрешение в заголовке сервера и атрибуте allow; по умолчанию разрешён свой источник. Подробности находятся в описании Permissions-Policy: screen-wake-lock. Мастерская встроена с собственного домена. Если политика браузера запрещает запрос во встроенном окне, откройте мастерскую отдельно по ссылке рядом с ней.

Не подменяйте отказ оптимистичной подписью «Включено». Сохраняйте имя ошибки в журнале разработчика, а посетителю давайте короткое объяснение и доступное следующее действие. Если точная причина неизвестна, так и обозначайте её: браузерные ограничения и настройки устройства не всегда различимы из кода страницы.

Как проверить результат на настоящем телефоне

Переключатель мобильной ширины проверяет расположение кнопок. Он не доказывает, что экран телефона останется включённым. Для этого нужен физический аппарат и выбранный браузер. Лучше записать короткий результат: устройство, версия браузера, механизм, настройки и что произошло.

  1. Проверьте обычное поведение. Откройте страницу без режима, положите телефон и узнайте, через какое время обычно гаснет экран. Не меняйте настройки посреди сравнения.
  2. Включите режим. Выберите срок длиннее обычного ожидания, оставьте вкладку видимой и не касайтесь дисплея. Смотрите на фактический результат.
  3. Остановите режим. Повторите ожидание после нажатия остановки. Проверьте, что удержание больше не продолжается.
  4. Пройдите прерывания. Переключите приложение, вернитесь, выключите режим, попробуйте ручную блокировку. Страница должна объяснять своё состояние.
  5. Проверьте другой путь. Если есть несколько целевых браузеров или используется WebView, повторите сценарий там. Зафиксируйте отказ как результат проверки.

Разделяйте результат испытания и общий вывод. «В Chrome на этом телефоне, при таких настройках, запрос сработал» — полезная запись. «Работает везде» — предположение, которое этот опыт не подтверждает. Для рабочего применения добавьте наблюдение при энергосбережении, при пониженном заряде и после длительного просмотра.

Частые вопросы и идеи для собственного проекта

Можно ли запускать режим сразу при открытии сайта?

Для NoSleep.js вызов включения следует привязывать к действию пользователя. Для интерфейса это также удобная договорённость: человек понимает, что изменилось. Не включайте удержание только потому, что посетитель случайно открыл статью.

Можно ли держать приложение работающим после выключения экрана?

Удержание подсветки и фоновая работа — разные задачи. Этот пример не гарантирует выполнение таймеров, запросов и синхронизации после ухода со страницы. Если данные важны, отдельно проектируйте сохранение, возобновление и восстановление после прерывания.

Что выбрать для новой панели?

Начните с целевого сценария и поддерживаемых браузеров. Когда достаточно нативного API, работа с объектом запроса делает статус прозрачнее. Если нужен запасной видеопуть, исследуйте NoSleep.js и проверяйте его на ваших устройствах. Обычный просмотр страницы должен оставаться полезным в обоих случаях.

Как расширить пример?

Добавьте время действия рядом с кнопкой, отдельный режим показа QR-кода или пошаговую инструкцию с сохранением текущего шага. Полезное развитие — понятнее завершать задачу и лучше объяснять отказ. Новые украшения сами по себе не решают эти вопросы.

Что взять в свой проект. Явное включение, видимую остановку, обработку ошибок, реакцию на скрытие и проверку на телефоне. Исходники мастерской помогают пройти этот маршрут без отдельной настройки демонстрации.