Видео компоненты

Atomico — это современная библиотека для построения веб-компонентов на JavaScript с реактивной моделью данных, похожей на React, но с упором на Web Components. Видео-компоненты в Atomico позволяют создавать интерактивные плееры с кастомной логикой управления, обработкой событий и интеграцией с другими частями приложения.


Создание базового видео-компонента

В Atomico компонент создается через функцию component. Для видео-компонента минимальный пример выглядит так:

import { component, html, useState } from "atomico";

function VideoPlayer() {
    const [playing, setPlaying] = useState(false);
    let videoRef;

    const togglePlay = () => {
        if (!videoRef) return;
        if (playing) {
            videoRef.pause();
        } else {
            videoRef.play();
        }
        setPlaying(!playing);
    };

    return html`
        <div>
            <video 
                ref=${(el) => videoRef = el} 
                src="sample.mp4" 
                width="640" 
                controls>
            </video>
            <button oncl ick=${togglePlay}>
                ${playing ? "Pause" : "Play"}
            </button>
        </div>
    `;
}

customElements.define("video-player", component(VideoPlayer));

Ключевые моменты:

  • useState управляет состоянием воспроизведения.
  • ref используется для получения ссылки на DOM-элемент <video>.
  • html позволяет возвращать шаблон с реактивными переменными.

Работа с событиями видео

Видео-тэг поддерживает множество событий: play, pause, ended, timeupdate и другие. В Atomico их удобно обрабатывать через on<Event> в шаблоне или через addEventListener на ссылке ref.

Пример отслеживания прогресса воспроизведения:

function VideoProgress() {
    const [time, setTime] = useState(0);
    let videoRef;

    const handleTimeUpdate = (e) => {
        setTime(videoRef.currentTime);
    };

    return html`
        <div>
            <video 
                ref=${(el) => videoRef = el} 
                src="sample.mp4" 
                width="640" 
                ontimeUPDATE=${handleTimeUpdate}>
            </video>
            <div>Прошло времени: ${time.toFixed(2)} сек</div>
        </div>
    `;
}

Особенности:

  • События можно привязывать напрямую в шаблоне через атрибуты on<Event>.
  • Обновление состояния через useState автоматически перерисовывает компонент.

Управление свойствами видео через Props

Atomico позволяет объявлять свойства компонента через props, что упрощает повторное использование:

function VideoPlayer({ src, width = 640 }) {
    const [playing, setPlaying] = useState(false);
    let videoRef;

    const togglePlay = () => {
        if (!videoRef) return;
        if (playing) videoRef.pause();
        else videoRef.play();
        setPlaying(!playing);
    };

    return html`
        <div>
            <video ref=${(el) => videoRef = el} src=${src} width=${width}></video>
            <button oncl ick=${togglePlay}>
                ${playing ? "Pause" : "Play"}
            </button>
        </div>
    `;
}

VideoPlayer.props = {
    src: String,
    width: Number
};

customElements.define("video-player", component(VideoPlayer));

Преимущества:

  • Легко менять видео-файл и размеры без изменения логики компонента.
  • Свойства могут иметь типизацию и значения по умолчанию.

Добавление кастомных контролов

Можно полностью отказаться от стандартных контролов <video> и создать свои кнопки управления:

function CustomControlsVideo({ src }) {
    const [playing, setPlaying] = useState(false);
    const [volume, setVolume] = useState(1);
    let videoRef;

    const togglePlay = () => {
        if (!videoRef) return;
        if (playing) videoRef.pause();
        else videoRef.play();
        setPlaying(!playing);
    };

    const changeVolume = (e) => {
        if (!videoRef) return;
        videoRef.volume = e.target.value;
        setVolume(e.target.value);
    };

    return html`
        <div>
            <video ref=${(el) => videoRef = el} src=${src} width="640"></video>
            <div>
                <button oncl ick=${togglePlay}>
                    ${playing ? "Pause" : "Play"}
                </button>
                <input type="range" min="0" max="1" step="0.01" value=${volume} oncha nge=${changeVolume}>
            </div>
        </div>
    `;
}

CustomControlsVideo.props = { src: String };

customElements.define("custom-video-player", component(CustomControlsVideo));

Особенности реализации:

  • input type="range" управляет громкостью.
  • Реактивное состояние volume синхронизирует ползунок и видео.
  • Кнопка управления воспроизведением полностью заменяет стандартные контролы.

Подключение нескольких видео-компонентов

Atomico позволяет легко использовать несколько экземпляров компонентов с разными свойствами:

<video-player src="video1.mp4" width="320"></video-player>
<video-player src="video2.mp4" width="480"></video-player>
<custom-video-player src="video3.mp4"></custom-video-player>

Каждый компонент сохраняет собственное состояние playing, volume и другие параметры, не влияя на остальные экземпляры.


Асинхронная загрузка и события готовности

Для более сложных видео-плееров важно отслеживать, когда видео полностью загружено:

function AsyncVideo({ src }) {
    const [ready, setReady] = useState(false);
    let videoRef;

    const handleLoadedData = () => setReady(true);

    return html`
        <div>
            <video ref=${(el) => videoRef = el} src=${src} width="640" onloadedd ata=${handleLoadedData}></video>
            ${ready ? html`<div>Видео готово к воспроизведению</div>` : html`<div>Загрузка...</div>`}
        </div>
    `;
}

AsyncVideo.props = { src: String };
customElements.define("async-video", component(AsyncVideo));

Преимущества:

  • Позволяет показывать индикатор загрузки.
  • Можно интегрировать с другими асинхронными процессами, например, с API для субтитров или аналитики.

Интеграция с CSS и анимациями

Atomico поддерживает scoped CSS через шаблон css или внешние стили. Для видео-компонентов удобно использовать CSS-переменные и анимации при изменении состояния:

import { css } from "atomico";

const videoStyle = css`
    video {
        border-radius: 12px;
        transition: transform 0.3s;
    }
    video:hover {
        transform: scale(1.05);
    }
    button {
        margin-top: 8px;
        padding: 4px 12px;
        border: none;
        background-color: #007acc;
        color: white;
        cursor: pointer;
        border-radius: 4px;
    }
`;

function StyledVideo({ src }) {
    const [playing, setPlaying] = useState(false);
    let videoRef;

    const togglePlay = () => {
        if (!videoRef) return;
        if (playing) videoRef.pause();
        else videoRef.play();
        setPlaying(!playing);
    };

    return html`
        <div class=${videoStyle}>
            <video ref=${(el) => videoRef = el} src=${src} width="640"></video>
            <button oncl ick=${togglePlay}>${playing ? "Pause" : "Play"}</button>
        </div>
    `;
}

StyledVideo.props = { src: String };
customElements.define("styled-video", component(StyledVideo));

Результат:

  • Видео плавно увеличивается при наведении.
  • Кнопка управляется через Atomico и полностью интегрирована с реактивной моделью.

Этот подход позволяет строить гибкие, масштабируемые и полностью настраиваемые видео-компоненты, интегрируемые в любой веб-проект с минимальными зависимостями и высокой реактивностью.