GrVideoPlayer

Пакет: @feugene/granularity-mediaспутникГруппа: Прочее

Берут, когда ролик внутри страницы продукта.

Когда брать

  • ролик внутри страницы продукта — обзор возможностей, где вид плеера обязан совпадать с остальным интерфейсом;
  • запись встречи или урока в личном кабинете: нужна перемотка с клавиатуры и предсказуемая позиция;
  • короткое видео-подтверждение в заявке — снятое тут же и проигранное рядом;
  • тёмная и светлая тема на одной странице: нативные элементы управления тему не знают.

Когда взять другое

НужноБерите
Снять кадр камеройGrCameraCapture
Показать картинку во весь экранGrImageViewer
Плитка вложения в лентеGrFilePreview

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

requestFullscreen на самом <video> отдаёт браузеру его собственный интерфейс: наши кнопки, подписи и клавиатура исчезают ровно там, где нужны больше всего — на весь экран. Поэтому запрос идёт у корневого элемента, и панель едет вместе с кадром.

Полоса буфера берётся вокруг позиции

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

Длительность известна не всегда

У потоковой записи — сделанной MediaRecorder, пришедшей из эфира — длительности нет в заголовке, и браузер отдаёт NaN. Полоса прогресса в этом случае не рисуется вовсе, а подпись показывает одно текущее время: «1:05 / 0:00» обещало бы конец, которого запись не знает.

Время в подписи, а не доля

Дорожка объявляет себя диктору через aria-valuetext — «1:05 / 2:00». Голое aria-valuenow дало бы «65 из 120»: формально верно и бесполезно.

Секунды всегда двузначные (1:05, не 1:5), а часы появляются, только когда они есть: 0:00:07 у семисекундного ролика читается как ошибка.

Звук выключен, если нужен автозапуск

autoplay без muted браузеры блокируют — политика автовоспроизведения не обходится ничем. Компонент этого не скрывает: muted остаётся отдельным пропом, и решение «запускать без звука» принимает приложение, а не библиотека.

Границы

  • нет качества и дорожек. Выбор потока (HLS, DASH), субтитры и звуковые дорожки — работа медиа-движка, а не элементов управления; их подключают сторонним плеером;
  • нет плейлиста. Компонент показывает один ролик; очередь и переходы между роликами остаются приложению;
  • нет скорости воспроизведения. Она нужна редко и стоит ещё одного меню на панели, где место дороже.

Установка

npm i @feugene/granularity-media

Импорт

import { GrVideoPlayer } from '@feugene/granularity-media/components/GrVideoPlayer'

API

API этого компонента ещё не посчитан: генератор витрины пока обходит только ядро. Пока его нет, справочник — в документации пакета.

Примеры 1

Basic

Basic
<script setup lang="ts">
import { ref } from 'vue'

import { GrBadge } from '@feugene/granularity'

/**
 * Ролик лежит в `public/demo` и собирается скриптом
 * `scripts/generate-demo-video.mjs`: витрина обязана работать без сети, а
 * тащить бинарь из внешнего источника — значит зависеть от чужого хостинга.
 */
const source = `${import.meta.env.BASE_URL}demo/sample.webm`

const state = ref<'ready' | 'playing' | 'paused' | 'ended'>('ready')
const position = ref(0)

function onTime(current: number) {
  position.value = current
}
</script>

<template>
  <div class="grid gap-4 lg:grid-cols-[minmax(0,1fr)_minmax(0,300px)]">
    <GrVideoPlayer
      :src="source"
      :aspect-ratio="16 / 9"
      muted
      @play="state = 'playing'"
      @pause="state = 'paused'"
      @ended="state = 'ended'"
      @timeupdate="onTime"
    />

    <div class="showcase-demo-panel grid content-start gap-3 rounded-[var(--gr-radius-lg)] border p-4">
      <p class="showcase-demo-text text-sm">
        Состояние: <GrBadge size="sm" tone="neutral">{{ state }}</GrBadge>
      </p>
      <p class="showcase-demo-text text-sm">
        Позиция: <strong>{{ position.toFixed(1) }} с</strong>
      </p>

      <p class="showcase-demo-text text-sm">
        Элементы управления свои, а не браузерные: нативные выглядят по-разному в каждом
        браузере и не знают ни про темы, ни про размеры дизайн-системы.
      </p>

      <p class="showcase-demo-text text-sm">
        Клавиатура работает, когда плеер в фокусе: пробел — пуск и пауза, стрелки влево и
        вправо — перемотка на пять секунд, <code>Home</code> и <code>End</code> — к началу и
        концу, <code>M</code> — звук, <code>F</code> — во весь экран.
      </p>

      <p class="showcase-demo-text text-sm">
        Длительность плеер берёт у браузера и не выдумывает: у потоковой записи её в заголовке
        нет, и тогда вместо «1:05 / 0:00» показывается одно текущее время, а полоса не рисуется
        вовсе — обещать конец, которого запись не знает, нельзя.
      </p>
    </div>
  </div>
</template>

Документация компонентаВсе компоненты