TextTracks

预计阅读时间: 5 分钟

TextTracks 功能提供对 Video.js 播放器字幕和文本轨道(包括章节缩略图)的完整状态管理和控制。

概述

TextTracks 功能是 Video.js React 中用于管理文本轨道(字幕、章节等)的模块。它提供文本轨道状态信息和字幕切换操作,支持 VTT、TTML 等多种字幕格式。

状态

属性类型描述
chaptersCuesVTTCue[]章节提示点数组
thumbnailCuesVTTCue[]缩略图提示点数组
thumbnailTrackSrcstring | null缩略图轨道源 URL
textTrackListTextTrack[]可用的文本轨道列表
subtitlesShowingboolean字幕是否正在显示

状态说明

  • chaptersCues:视频中的章节标记点,用于章节导航
  • thumbnailCues:鼠标悬停时显示的缩略图提示点
  • thumbnailTrackSrc:缩略图精灵图的源地址
  • textTrackList:所有可用的文本轨道(字幕、语言等)
  • subtitlesShowing:当前是否有字幕正在显示

操作

操作类型描述
toggleSubtitlesAction切换字幕显示状态

toggleSubtitles

切换字幕的显示/隐藏状态。

import { usePlayerActions, usePlayer } from '@videojs/react';
import { selectTextTracks } from '@videojs/react';

function SubtitleToggle() {
  const textTracks = usePlayer(selectTextTracks);
  const { toggleSubtitles } = usePlayerActions();
  
  return (
    <button onClick={toggleSubtitles}>
      字幕: {textTracks.subtitlesShowing ? '开启' : '关闭'}
    </button>
  );
}

选择器

传递 selectTextTracksusePlayer 钩子以订阅文本轨道状态。

import { selectTextTracks, usePlayer } from '@videojs/react';

function TextTracksStatus() {
  const textTracks = usePlayer(selectTextTracks);
  
  return (
    <div>
      <p>字幕显示: {textTracks.subtitlesShowing ? '是' : '否'}</p>
      <p>可用轨道: {textTracks.textTrackList.length}</p>
      <p>章节数: {textTracks.chaptersCues.length}</p>
      <p>缩略图数: {textTracks.thumbnailCues.length}</p>
    </div>
  );
}

示例

字幕选择器

import { selectTextTracks, usePlayer, usePlayerActions } from '@videojs/react';

function SubtitleSelector() {
  const textTracks = usePlayer(selectTextTracks);
  
  // 获取字幕轨道
  const subtitleTracks = textTracks.textTrackList.filter(
    track => track.kind === 'subtitles' || track.kind === 'captions'
  );
  
  return (
    <div className="subtitle-selector">
      <h4>字幕选择</h4>
      {subtitleTracks.length === 0 ? (
        <p>无可用字幕</p>
      ) : (
        <ul>
          {subtitleTracks.map((track, index) => (
            <li key={index}>
              <span>{track.label || track.language}</span>
              {track.mode === 'showing' && <span className="active">*</span>}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

章节导航

import { selectTextTracks, usePlayer, usePlayerActions } from '@videojs/react';

function ChapterNavigation() {
  const textTracks = usePlayer(selectTextTracks);
  const { seek } = usePlayerActions();
  
  if (!textTracks.chaptersCues.length) {
    return null;
  }
  
  return (
    <div className="chapter-navigation">
      <h4>章节导航</h4>
      <ul>
        {textTracks.chaptersCues.map((cue, index) => (
          <li key={index}>
            <button onClick={() => seek(cue.startTime)}>
              {cue.text}
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

字幕切换按钮

import { selectTextTracks, usePlayer, usePlayerActions } from '@videojs/react';

function SubtitleToggleButton() {
  const textTracks = usePlayer(selectTextTracks);
  const { toggleSubtitles } = usePlayerActions();
  
  // 检查是否有可用的字幕轨道
  const hasSubtitles = textTracks.textTrackList.some(
    track => track.kind === 'subtitles' || track.kind === 'captions'
  );
  
  return (
    <button 
      onClick={toggleSubtitles}
      disabled={!hasSubtitles}
      className={textTracks.subtitlesShowing ? 'active' : ''}
    >
      {textTracks.subtitlesShowing ? 'CC ON' : 'CC OFF'}
    </button>
  );
}

缩略图预览

import { selectTextTracks, selectTime, usePlayer } from '@videojs/react';
import { useState } from 'react';

function ThumbnailPreview() {
  const textTracks = usePlayer(selectTextTracks);
  const time = usePlayer(selectTime);
  const [hoveredThumbnail, setHoveredThumbnail] = useState<string | null>(null);
  
  // 找到当前时间对应的缩略图
  const currentThumbnail = textTracks.thumbnailCues.find(
    cue => time.currentTime >= cue.startTime && time.currentTime < cue.endTime
  );
  
  if (!textTracks.thumbnailTrackSrc || !currentThumbnail) {
    return null;
  }
  
  // 计算缩略图在精灵图中的位置
  const getThumbnailStyle = () => {
    // 假设缩略图是 160x90 像素
    const thumbWidth = 160;
    const thumbHeight = 90;
    const columns = 5; // 精灵图的列数
    
    const index = textTracks.thumbnailCues.indexOf(currentThumbnail);
    const col = index % columns;
    const row = Math.floor(index / columns);
    
    return {
      backgroundImage: `url(${textTracks.thumbnailTrackSrc})`,
      backgroundPosition: `-${col * thumbWidth}px -${row * thumbHeight}px`,
      width: `${thumbWidth}px`,
      height: `${thumbHeight}px`
    };
  };
  
  return (
    <div className="thumbnail-preview">
      <div className="thumbnail" style={getThumbnailStyle()}></div>
    </div>
  );
}

语言选择器

import { selectTextTracks, usePlayer } from '@videojs/react';

function LanguageSelector() {
  const textTracks = usePlayer(selectTextTracks);
  
  const languages = [...new Set(
    textTracks.textTrackList.map(track => track.language)
  )];
  
  return (
    <div className="language-selector">
      <h4>字幕语言</h4>
      <select>
        <option value="">关闭</option>
        {languages.map((lang, index) => (
          <option key={index} value={lang}>
            {lang.toUpperCase()}
          </option>
        ))}
      </select>
    </div>
  );
}

注意事项

  1. 轨道加载:字幕轨道需要在视频加载时或通过 src 属性指定
  2. 格式支持:Video.js 原生支持 WebVTT 格式
  3. 多语言:可以同时有多个语言的字幕轨道
  4. 缩略图:缩略图预览需要额外的 VTT 扩展支持
  5. 样式定制:字幕样式可以通过 CSS 或 VTT 设置文件自定义