Сайты с ИИ2026-08-318 минНатали Анарбаева

Как сделать камеру на сайте: доступ, снимок и проверка

Как сделать камеру на сайте: доступ, снимок и проверка

Камеру на сайте подключают через браузерный метод navigator.mediaDevices.getUserMedia(). Страница показывает живой предпросмотр только после явного разрешения человека. Для рабочего результата понадобятся HTTPS, кнопка запуска, элемент video, обработка отказа и остановка камеры после завершения.

Уточните, что именно должна делать камера

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

Правило выбора такое. Если человеку нужно только приложить готовый документ или фотографию, используйте обычное поле файла с accept="image/*" и возможностью камеры на телефоне. Живой видеопоток нужен, когда важны кадрирование, сканирование, примерка или мгновенный снимок. Для простой загрузки сложный доступ к камере создаёт лишний отказ.

Не открывайте запрос камеры при загрузке страницы. Сначала объясните, зачем нужен доступ, и запускайте системное окно разрешения только после нажатия понятной кнопки.

Подготовьте обязательные условия

Метод getUserMedia() работает в защищённом контексте. На обычном удалённом сайте это означает HTTPS. На локальной разработке браузер разрешает использовать localhost. Пользовательское разрешение обязательно, а встроенному фрейму может понадобиться отдельное разрешение allow="camera". Эти ограничения описаны в официальной справке MDN по getUserMedia.

  • Сайт открывается по https://, сертификат действителен.
  • Кнопка до запроса объясняет действие: Включить камеру.
  • Рядом есть обычная загрузка файла как запасной путь.
  • Страница не обещает, что снимок сохранён, пока сервер действительно его не принял.
  • После снимка есть кнопки Переснять, Скачать или Отправить, соответствующие реальному сценарию.
  • После закрытия окна видеопоток останавливается.

Если камера находится внутри виджета на чужом домене, одной строки JavaScript недостаточно. Родительский сайт должен разрешить функцию фрейму, а сервер может ограничивать её заголовком Permissions Policy. Для первого прототипа надёжнее разместить камеру на странице верхнего уровня.

Доступ к камере это одна функция страницы. Рабочий сайт ещё должен объяснить действие, сохранить доверие и корректно завершить сценарий.

Занять место

Добавьте разметку предпросмотра

Минимальная разметка содержит кнопку запуска, video, холст для кадра и место для ошибки. Атрибут playsinline не даёт мобильному браузеру обязательно уводить видео в полноэкранный режим. muted нужен, чтобы предпросмотр не создавал аудиопетлю, хотя в этом примере звук вообще не запрашивается.

<button id="start" type="button">Включить камеру</button>
<video id="preview" playsinline muted></video>
<button id="shot" type="button" disabled>Сделать снимок</button>
<button id="stop" type="button" disabled>Выключить камеру</button>
<canvas id="frame" hidden></canvas>
<p id="status" role="status"></p>

Не скрывайте системный индикатор камеры и не маскируйте его собственным интерфейсом. Человек должен понимать, когда поток активен. Если страница собирается ИИ-ассистентом, перечислите эти элементы в задании. Общий порядок вставки скриптов и виджетов есть в гайде по добавлению функций на сайт.

По теме: Как сделать 3D-сайт: модель, загрузка и запасной экран

Запустите и остановите видеопоток

Запрашивайте только видео. Ограничение facingMode: { ideal: "environment" } просит мобильный браузер выбрать заднюю камеру, но не делает её обязательной. Это безопаснее жёсткого требования: ноутбук с одной камерой всё равно сможет открыть поток.

const video = document.querySelector('#preview');
const start = document.querySelector('#start');
const shot = document.querySelector('#shot');
const stop = document.querySelector('#stop');
const status = document.querySelector('#status');
let stream;

start.addEventListener('click', async () => {
  status.textContent = '';
  try {
    stream = await navigator.mediaDevices.getUserMedia({
      video: { facingMode: { ideal: 'environment' } },
      audio: false,
    });
    video.srcObject = stream;
    await video.play();
    shot.disabled = false;
    stop.disabled = false;
  } catch (error) {
    status.textContent = 'Не удалось включить камеру: ' + error.name;
  }
});

function stopCamera() {
  stream?.getTracks().forEach((track) => track.stop());
  video.srcObject = null;
  stream = undefined;
  shot.disabled = true;
  stop.disabled = true;
}
stop.addEventListener('click', stopCamera);
window.addEventListener('pagehide', stopCamera);

Остановка через track.stop() обязательна не из-за оформления. Пока поток жив, индикатор камеры может оставаться активным, а другой сайт или приложение не всегда получит устройство. Вызывайте остановку после отправки, при закрытии модального окна и при уходе со страницы.

Сделайте снимок без немедленной отправки

Кадр копируется из video в canvas. Сначала дождитесь, пока у видео появятся реальные videoWidth и videoHeight. Затем нарисуйте кадр методом drawImage() и превратите холст в файл через toBlob(). Метод создаёт объект Blob, а формат и качество можно указать отдельно, это подтверждает справка MDN по toBlob.

const canvas = document.querySelector('#frame');

shot.addEventListener('click', () => {
  if (!video.videoWidth) {
    status.textContent = 'Камера ещё не готова. Подождите секунду.';
    return;
  }
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  canvas.getContext('2d').drawImage(video, 0, 0);

  canvas.toBlob((blob) => {
    if (!blob) {
      status.textContent = 'Не удалось создать снимок.';
      return;
    }
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = 'photo.jpg';
    link.click();
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  }, 'image/jpeg', 0.9);
});

Этот пример скачивает фотографию на устройство и никуда её не отправляет. Не подписывайте кнопку как Загрузить, если на сервере нет обработчика. Для серверной отправки создайте отдельный FormData, покажите предпросмотр и отправляйте только после дополнительного подтверждения. Схема безопасного API-запроса разобрана в статье о запросах на сайте.

Даже короткая функция с камерой требует понятной структуры и проверки на телефоне. На бесплатном вебинаре показываем, как собирать такие сайты с ИИ и доводить их до публикации.

Смотреть сборку сайта

Проверьте приватность и понятный выход

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

  1. Откройте страницу в новой приватной вкладке и нажмите Включить камеру.
  2. Разрешите доступ один раз: предпросмотр появляется, звук не запрашивается.
  3. Нажмите Сделать снимок: файл открывается или скачивается, а ориентация кадра выглядит правильно.
  4. Нажмите Выключить камеру: системный индикатор гаснет.
  5. Повторите проверку с отказом в разрешении: страница показывает понятный текст и предлагает загрузить файл.
  6. Проверьте телефон с передней и задней камерой, затем поверните экран.
  7. Уйдите со страницы во время предпросмотра: поток завершается.

По теме: Как сделать сайт на iPad в 2026: пошагово без компьютера

Диагностика частых сбоев

navigator.mediaDevices отсутствует. Сначала проверьте адрес страницы. На удалённом сайте нужен HTTPS. Затем убедитесь, что код выполняется в обычной странице с происхождением, а не в ограниченном фрейме или случайном HTML-просмотрщике.

Браузер возвращает NotAllowedError. Пользователь отказал, разрешение заблокировано в настройках сайта либо фрейму не дали доступ. Не повторяйте системный запрос в цикле. Покажите инструкцию по настройкам и запасную загрузку файла.

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

Есть OverconstrainedError. Код потребовал параметр, которого камера не поддерживает. Замените обязательное значение на ideal или сначала запросите просто { video: true }, затем добавляйте ограничения по одному.

Видео чёрное, а ошибка не появилась. Дождитесь video.play() и проверьте videoWidth. Убедитесь, что у элемента есть видимый размер, а другое приложение не удерживает камеру. После прошлой попытки остановите старые треки.

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

Когда этот способ не подходит

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

Частые вопросы

Можно открыть камеру на сайте без разрешения?

Нет. Браузер запрашивает разрешение пользователя. Сайт может объяснить пользу заранее, но не должен обходить системный выбор.

Почему камера работает на localhost, но не на сайте?

Локальная разработка считается допустимым защищённым контекстом, а удалённой странице нужен HTTPS с корректным сертификатом.

Как сразу открыть заднюю камеру телефона?

Укажите facingMode: { ideal: "environment" }. Это предпочтение, а не гарантия. Если нужен выбор устройства, после разрешения получите список камер и покажите понятный переключатель.

Снимок автоматически загружается на сервер?

Нет. canvas.toBlob() только создаёт объект изображения в браузере. Для отправки нужен отдельный запрос и явное действие пользователя.

Можно сделать камеру на конструкторе сайтов?

Только если конструктор позволяет вставить JavaScript и не блокирует разрешения камеры. Внутри чужого фрейма могут понадобиться настройки владельца платформы. Если доступа к ним нет, используйте отдельную HTTPS-страницу.

Камера это одна функция, а доверие создаёт вся страница целиком. На воркшопе «Сайты с ИИ» показываем, как собрать и опубликовать работающий сайт, даже если вы не пишете код вручную.

Смотреть воркшоп
Разборы, кейсы и фишки вайбкодинга каждую неделю в телеграм-канале «Яков вайбкодит».
Первый проект с ИИ: живой разбор, бесплатно Занять место