Привет, друзья! В этой статье я покажу вам, как делать селфи в браузере. Мы разработаем простое приложение со следующим функционалом: Репозиторий. Приложение будет разработано на чистом JavaScript. Для создания шаблона приложения будет использован Vite. Если вам это интересно, прошу под кат. Данная статья является четвертой в серии статей, посвященных работе с медиа в браузере: При разработке приложения мы будем опираться на следующие спецификации: Ссылки на соответствующие разделы MDN будут приводиться по мере необходимости. Обратите внимание: основная технология, которую мы будем рассматривать, является экспериментальной. На сегодняшний день она поддерживается только 71% браузеров. Поэтому говорить о возможности ее использования в продакшне рано. Можно сказать, что эта технология относится к категории "Веб завтрашнего дня". Создаем шаблон приложения: Пока создается шаблон, поговорим об интерфейсах и методах, которые мы будем использовать. Для захвата потока из устройств пользователя используется метод getUserMedia расширения MediaDevices интерфейса Navigator: Поток состоит из треков (одного и более). Для получения треков используются методы getTrackById, getTracks, getAudioTracks и getVideoTracks. Мы будем использовать только последний: Для получения списка возможностей и настроек трека используются методы getCapabilities и getSettings, соответственно: Для применения к треку (дополнительных или продвинутых) ограничений используется метод applyConstraints: Для захвата изображений и фреймов из видеотрека используется интерфейс ImageCapture: Для получения списка возможностей и настроек для фото используются методы getPhotoCapabilities и getPhotoSettings, соответственно: Для получения снимка используется метод takePhoto. Данный метод возвращает Blob: Для создания ссылки на blob используется метод createObjectUrl интерфейса URL: Для освобождения (release) ссылки на blob используется метод revokeObjectUrl: Для захвата фрейма используется метод grabFrame. Данный метод возвращает ImageBitmap: Для рендеринга imageBitmap на холсте используется метод drawImage: Для извлечения пиксельных данных (pixel data) из холста используется метод getImageData. Данный метод возвращает ImageData: Наконец, для рендеринга ImageData на холсте используется метод putImageData: Шаблон давно готов и с нетерпением ждет, когда мы приступим к разработке приложения. Структура приложения будет следующей: Начнем с разметки (index.html): Индикатор загрузки вместе со стилями (styles/loader.css) я взял отсюда. Добавим немного красоты (styles/main.css): Переходим к скриптам. Начнем с определения глобальных утилит и переменных (scripts/globals.js). Определяем утилиту для получения ссылок на элементы: Определяем утилиту для создания элементов. Мы могли бы сделать это так: Но лучше сделаем так: Определяем функцию для рендеринга элементов: Мы будем довольно много манипулировать атрибутами элементов, поэтому имеет смысл определить для этого соответствующие утилиты: Получаем ссылки на DOM-элементы: Создаем еще несколько глобальных переменных: И определяем (продвинутые) настройки для трека: Мы берем настройки, которые можно использовать в диапазонных полях. С полным списком настроек для трека можно ознакомиться здесь. Рассмотрим утилиту для захвата видеопотока и подготовки инпутов (scripts/utils/getMedia.js). Определяем дефолтные ограничения потока: Захватываем видеопоток, передаем его в элемент video с помощью srcObject, извлекаем видеотрек и создаем экземпляр ImageCapture: Получаем списки возможностей и настроек фото и трека: Перебираем ключи "словаря" с настройками: Если трек поддерживает настройку: Далее мы делаем следующее: Наконец, скрываем индикатор загрузки и отображаем приложение: Рассмотрим утилиту для получения снимка (scripts/utils/takePhoto.js). Определяем дефолтные настройки для фото: Применяем настройки, установленные пользователем: Получаем blob с помощью метода takePhoto, формируем на него ссылку и передаем ее в элементы img и a: Рассмотрим утилиту для захвата фрейма (scripts/utils/grabFrame.js): Получаем ImageBitmap с помощью метода grabPhoto, определяем ширину и высоту холста и рендерим фрейм: Читаем пиксели и инвертируем цвета: Перерисовываем изображение и генерируем ссылку для скачивания фрейма: Для формирования ссылки на фрейм мы используем метод toDataURL, возвращающий URL с префиксом data:, который позволяет встраивать небольшие файлы прямо в документ, например, изображение, преобразованное в строку base64. Осталось разобраться с основным модулем приложения (scripts/main.js). Импортируем утилиты и запускаем захват медиа: Регистрируем обработчик отправки формы — применения пользовательских настроек и получение снимка: Регистрируем обработчик нажатия кнопок: Протестируем работоспособность нашего приложения. Запускаем сервер для разработки с помощью команды yarn dev или npm run dev и открываем вкладку браузера по адресу http://localhost:3000: 
 Нажимаем на кнопку Take photo: 
 Получаем селфи и ссылку для его скачивания. Удаляем фото, нажав на кнопку Remove photo. Меняем настройки и снова нажимаем Take photo: 
 Обратите внимание: применение к треку продвинутых настроек повлияло также на видео, поскольку речь идет об одном и том же треке. Удаляем фото, возвращаем настройки в исходное состояние и нажимаем на кнопку Grab frame: 
 Получаем инвертированное изображение и ссылку для его скачивания. Очищаем холст, нажав на кнопку Clear canvas. Отлично! Наше приложение работает, как ожидается. Пожалуй, это все, чем я хотел поделиться с вами в данной статье. Благодарю за внимание и happy coding!  JavaScript Developer