React 框架使用说明

ZWPlayer 提供了针对 React 框架的专用接口包 zwplayer-react,支持 React 16.8+、17.x、18.x 和 19.x 版本。

1. 安装步骤

1.1 安装接口包

npm i zwplayer-react --save

1.2 验证安装

安装成功后,项目的 public 目录下会自动生成 zwplayer 核心库目录,该目录必须随项目一起构建发布。

1.3 版本要求

  • React 16.8+(需要 Hooks 支持)
  • 支持 Create React App、Vite、Next.js 等构建工具

2. 组件使用

2.1 基本使用示例

import React, { useRef } from 'react';
import { ZwPlayer } from 'zwplayer-react';

function VideoPlayer() {
  const playerRef = useRef(null);

  const movieUrl = 'https://cdn.zwplayer.cn/media/VMAP9lxJvRpgn5sP3lV6rQ9qkzQmh5psggso3185.mp4';

  const handlePlayerReady = () => {
    console.log('Player ready event.');
    const player = playerRef.current;
    // 播放器就绪后可以调用方法
  };

  const handlePlayerMediaEvent = (event) => {
    console.log('media event:', event.type);
  };

  return (
    <div className="player-container">
      <ZwPlayer
        ref={playerRef}
        nodeid="main-player"
        murl={movieUrl}
        onready={handlePlayerReady}
        onmediaevent={handlePlayerMediaEvent}
        autoplay={false}
        snapshotButton={true}
        optionButton={true}
        infoButton={true}
        enableDanmu={true}
        chapterButton={true}
        fluid={true}
        disableMutedConfirm={true}
      />
    </div>
  );
}

export default VideoPlayer;

2.2 弹幕功能示例

import React, { useRef } from 'react';
import { ZwPlayer } from 'zwplayer-react';

function DanmuPlayer() {
  const playerRef = useRef(null);

  const movieUrl = 'https://cdn.zwplayer.cn/media/VMAP9lxJvRpgn5sP3lV6rQ9qkzQmh5psggso3185.mp4';

  const handlePlayerReady = () => {
    console.log('Player ready event.');
    const player = playerRef.current;

    // 添加测试弹幕
    setTimeout(() => {
      if (player && player.appendDanmu) {
        player.appendDanmu({
          border: '1px solid #ccc',
          text: '欢迎来到 ZWPlayer 弹幕演示!',
          color: '#ff6b6b'
        });
      }
    }, 3000);
  };

  const handleSendDanmu = (danmuText) => {
    if (!danmuText) return;

    const player = playerRef.current;
    let danmu;

    try {
      const jtext = JSON.parse(danmuText);
      danmu = {
        border: '1px solid #ccc',
        text: jtext['text']
      };
    } catch (e) {
      danmu = {
        border: '1px solid #ccc',
        text: danmuText
      };
    }

    if (player && player.appendDanmu) {
      player.appendDanmu(danmu);
    }
  };

  return (
    <div>
      <ZwPlayer
        ref={playerRef}
        murl={movieUrl}
        onready={handlePlayerReady}
        autoplay={false}
        sendDanmu={handleSendDanmu}
        enableDanmu={true}
        danmuBarId="danmu-controlbar"
        disableMutedConfirm={true}
        fluid={true}
      />

      <div id="danmu-controlbar" className="danmubar"></div>
    </div>
  );
}

export default DanmuPlayer;

弹幕控制栏样式:

.danmubar {
  height: 50px;
  background-color: #232323;
  padding: 8px;
  box-sizing: border-box;
  display: flex;
}

.danmubar .zwp_danmu-controlbar {
  width: 60%;
}

2.3 Logo 水印示例

import React, { useRef } from 'react';
import { ZwPlayer } from 'zwplayer-react';

function LogoPlayer() {
  const playerRef = useRef(null);

  const movieUrl = 'https://cdn.zwplayer.cn/media/VMAP9lxJvRpgn5sP3lV6rQ9qkzQmh5psggso3185.mp4';
  const poster = 'https://cdn.zwplayer.cn/media/b44c43c90be3521bc352aad1e80f9cd0_thumb.jpg';

  const logoConfig = {
    icon: 'https://cdn.zwplayer.cn/logo.png',
    dock: 'right',      // 位置:left, right
    x: '5%',            // 水平偏移
    y: '5%',            // 垂直偏移
    width: '10%',       // 宽度
    height: '10%',      // 高度
    opacity: 70         // 透明度 0-100
  };

  return (
    <ZwPlayer
      ref={playerRef}
      murl={movieUrl}
      logo={logoConfig}
      poster={poster}
      autoplay={false}
      disableMutedConfirm={true}
      fluid={true}
    />
  );
}

export default LogoPlayer;

2.4 缩略图示例

import React, { useRef } from 'react';
import { ZwPlayer } from 'zwplayer-react';

function ThumbnailPlayer() {
  const playerRef = useRef(null);

  const movieUrl = 'https://cdn.zwplayer.cn/media/VMAP9lxJvRpgn5sP3lV6rQ9qkzQmh5psggso3185.mp4';

  const thumbnails = {
    url: 'https://cdn.zwplayer.cn/media/b44c43c90be3521bc352aad1e80f9cd0_thumb.jpg',
    width: 160,
    height: 90,
    row: 9,
    col: 9,
    total: 74
  };

  return (
    <ZwPlayer
      ref={playerRef}
      murl={movieUrl}
      thumbnails={thumbnails}
      fluid={true}
    />
  );
}

export default ThumbnailPlayer;

2.5 章节分段示例

import React, { useRef } from 'react';
import { ZwPlayer } from 'zwplayer-react';

function ChapterPlayer() {
  const playerRef = useRef(null);

  const movieUrl = 'https://cdn.zwplayer.cn/media/VMAP9lxJvRpgn5sP3lV6rQ9qkzQmh5psggso3185.mp4';

  const handlePlayerReady = () => {
    const player = playerRef.current;

    const chapters = [
      {
        title: "分段1",
        desc: "分段1描述",
        time: 0,
        duration: 50,
        thumb: null
      },
      {
        title: "分段2",
        desc: "分段2描述",
        time: 50,
        duration: 100,
        style: { background: "blue" },
        image: null
      },
      {
        title: "分段3",
        desc: "分段3描述",
        time: 100,
        duration: 200,
        style: { background: "green" },
        image: null
      }
    ];

    if (player) {
      player.setChapters(chapters);
    }
  };

  return (
    <ZwPlayer
      ref={playerRef}
      murl={movieUrl}
      onready={handlePlayerReady}
      chapterButton={true}
      fluid={true}
    />
  );
}

export default ChapterPlayer;

2.6 路由切换(配合 React Router)

zwplayer-react 内置了对 react-router-dom 的支持。当安装了 react-router-dom 时,组件会自动监听路由变化并在路由切换前安全清理播放器资源,防止 DOM 访问错误。

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { ZwPlayer } from 'zwplayer-react';

function VideoPage() {
  const playerRef = useRef(null);

  return (
    <ZwPlayer
      ref={playerRef}
      murl="https://cdn.zwplayer.cn/media/video.mp4"
      fluid={true}
    />
  );
}

// 路由切换时播放器会自动安全销毁
function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/video" element={<VideoPage />} />
        <Route path="/other" element={<div>Other Page</div>} />
      </Routes>
    </BrowserRouter>
  );
}

注意react-router-dom 是可选依赖,未安装时不会影响播放器的正常使用。

3. 变动属性

ZwPlayer 组件的属性与原生 zwplayer 构造函数参数对象的属性大部分相同,但 zwplayer-react 对部分属性做了调整:

属性名称 说明 备注
murl 媒体地址参数(可以是 stringobjectarray 类型)。如果此属性在运行中发生变动,则组件会自动调用 play 方法打开新的地址。通过修改绑定的 state 变量,可以动态切换播放节目。 作用改动
nodeid 用于 ZwPlayer 组件顶级父节点 div 的 id,如果不提供,组件会自动生成一个。 新增
zwplayerlib zwplayer 库地址。zwplayer-react 底层采用动态加载原生 zwplayer 库的方式,不会随 React 的 build 命令将 zwplayer 底层库编译进项目。这样可以在项目编译发布后直接替换 zwplayer 库文件实现无缝升级。此参数可以缺省,缺省时使用 public 目录下的 zwplayer/zwplayer.js 路径。 新增
danmuBarId 用于定位 zwplayer 弹幕输入控制条的 div 元素的 id。提供此参数可以避免调用 buildDanmuControlbar 函数来创建弹幕控制条。控制条的 class 名为 zwp_danmu-controlbar,可以使用该 class 名控制弹幕控制条的风格。 新增
style 应用到播放器容器 div 上的内联样式对象。 新增
videoprop 视频元素属性对象,可以包含 urlvideostyle 属性。 新增
onready 播放器初始化完成回调函数,参数为播放器实例。 事件属性
onmediaevent 媒体事件回调函数,参数为 event 对象。 事件属性
onneterror 网络错误回调函数。 事件属性
onnetclose 网络关闭回调函数。 事件属性
onFileSelected 文件选择回调函数。 事件属性
onFileLoaded 文件加载完成回调函数。 事件属性
其它 请参见 zwplayer 播放器构造函数的参数说明。

常用属性说明

显示控制相关:

  • autoplay: 是否自动播放(boolean,默认 false
  • fluid: 是否启用流体布局(boolean,默认 false
  • snapshotButton: 是否显示截图按钮(boolean
  • optionButton: 是否显示选项按钮(boolean
  • infoButton: 是否显示信息按钮(boolean
  • chapterButton: 是否显示章节菜单按钮(boolean
  • enableDanmu: 是否启用弹幕功能(boolean

媒体相关:

  • murl: 媒体地址(stringobjectarray
  • poster: 封面图地址(string
  • thumbnails: 缩略图配置(object

功能相关:

  • sendDanmu: 弹幕发送回调函数(function
  • disableMutedConfirm: 是否禁用静音确认(boolean
  • keepAudioWindow: 是否保持音频窗口(boolean
  • localPlayback: 是否本地播放(boolean
  • recordButton: 是否显示录制按钮(boolean
  • segmentButton: 是否显示分段按钮(boolean

4. 方法调用

ZwPlayer 组件通过 React 的 forwardRef + useImperativeHandle 暴露播放器方法。使用 useRef 获取组件引用后即可调用。

4.1 获取播放器实例

import React, { useRef } from 'react';
import { ZwPlayer } from 'zwplayer-react';

function VideoPlayer() {
  const playerRef = useRef(null);

  const handlePlayerReady = () => {
    const player = playerRef.current;
    console.log('播放器实例:', player);
  };

  return (
    <ZwPlayer
      ref={playerRef}
      murl="video.mp4"
      onready={handlePlayerReady}
    />
  );
}

4.2 常用方法

// 播放/暂停/恢复
playerRef.current.play();
playerRef.current.pause();
playerRef.current.resume();
playerRef.current.stop();

// 跳转到指定时间(秒)
playerRef.current.seekTime(30);

// 添加弹幕
playerRef.current.appendDanmu({
  border: '1px solid #ccc',
  text: '这是一条弹幕',
  color: '#ff6b6b'
});

// 设置章节
playerRef.current.setChapters([{
  title: "分段1",
  desc: "分段1描述",
  time: 0,
  duration: 50
}]);

// 获取当前播放时间
const currentTime = playerRef.current.getCurrentTime();

// 获取视频总时长
const duration = playerRef.current.getDuration();

// 音量与静音
playerRef.current.setMute(true);

// 全屏
playerRef.current.setullscr(true);

// 字幕
playerRef.current.addSubtitle('https://example.com/subtitle.vtt', 0, '中文');
playerRef.current.removeSubtitle();

// 弹幕控制
playerRef.current.setEnableDanmu(true);

// 弹幕控制栏
playerRef.current.buildDanmuControlbar('danmu-bar-id', 'custom-class');

// 通知尺寸变化
playerRef.current.notifyResize(800, 450);

// 灯光控制
playerRef.current.lightOff();
playerRef.current.lightOn();

// 获取原始播放器实例(高级用法)
const rawPlayer = playerRef.current.player;

// 销毁播放器
playerRef.current.destroy();

5. StrictMode 兼容性

React 18 的 StrictMode 会在开发模式下触发两次 useEffect,可能导致播放器重复创建。zwplayer-react 内置了实例缓存机制来处理此问题:

  • 使用 window.__zwplayer_instances__ 全局 Map 跟踪播放器实例
  • 当检测到重复创建时自动复用已有实例
  • 组件卸载时安全清理所有资源(定时器、ResizeObserver、DOM 引用等)

注意:此机制在生产模式下不会触发额外开销。

6. 示例仓库

7. 注意事项

7.1 项目配置

  1. 目录结构: 安装前确保项目存在 public 目录
  2. 核心库: public/zwplayer 目录必须随项目一起发布
  3. 动态加载: zwplayer 采用动态加载机制,支持无缝升级,不会被打包进最终的 bundle
  4. 方法调用: 通过 useRef 引用调用播放器方法

7.2 最佳实践

  1. 使用条件渲染控制显示

    {showPlayer && (
      <ZwPlayer
        ref={playerRef}
        murl={movieUrl}
        fluid={true}
      />
    )}

    使用条件渲染(&& 或三元运算符)而不是 CSS 隐藏,可以确保播放器正确初始化和销毁。

  2. 等待播放器就绪后再调用方法

    const handlePlayerReady = () => {
      const player = playerRef.current;
      // 播放器就绪后才能调用方法
      player.seekTime(30);
    };
  3. 动态切换视频

    直接修改 murl 的 state 值,组件会自动重新加载:

    const [videoUrl, setVideoUrl] = useState('video1.mp4');
    // 切换视频
    setVideoUrl('video2.mp4');
  4. 响应式布局

    .player-container {
      width: 100%;
      max-width: 1280px;
      margin: 0 auto;
    }
    
    @media (max-width: 768px) {
      .player-container {
        width: 100%;
        height: auto;
        aspect-ratio: 16/9;
      }
    }

7.3 常见问题

Q: 为什么播放器无法显示?

A: 检查以下几点:

  1. 确保媒体 URL 可访问
  2. 检查浏览器控制台是否有错误信息
  3. 确认 public/zwplayer 目录存在且包含必需的文件
  4. 确认使用 {showPlayer && <ZwPlayer ... />} 而非 CSS 隐藏

Q: 如何动态切换视频?

A: 直接修改 murl 的 state 值:

const [videoUrl, setVideoUrl] = useState('video1.mp4');
setVideoUrl('new-video-url.mp4');

Q: StrictMode 下为什么会出现两个播放器?

A: 这是 React 18 StrictMode 的预期行为。zwplayer-react 内置了实例缓存机制来自动处理此问题,无需额外配置。

Q: 如何自定义弹幕控制栏样式?

A: 通过 CSS 类名直接控制:

.danmubar .zwp_danmu-controlbar {
  width: 60%;
  background-color: #232323;
}

Q: 如何获取播放器的当前状态?

A: 通过 useRef 获取组件引用后调用相应方法:

const player = playerRef.current;
const currentTime = player.getCurrentTime();
const duration = player.getDuration();

7.4 项目结构建议

Create React App 项目结构

your-react-project/
├── public/
│   └── zwplayer/           # 播放器核心库(自动生成,必须发布)
├── src/
│   ├── components/
│   │   └── VideoPlayer.jsx  # 播放器封装组件
│   ├── pages/
│   │   └── VideoPage.jsx    # 使用播放器的页面
│   ├── App.jsx
│   └── index.js
├── package.json
└── vite.config.js (或 react-scripts)

7.5 调试建议

  1. 监听播放器就绪事件

    const handlePlayerReady = () => {
      console.log('播放器已就绪:', playerRef.current);
    };
  2. 监听所有媒体事件

    const handlePlayerMediaEvent = (event) => {
      console.log('媒体事件:', event.type, event);
    };
  3. 使用浏览器开发者工具

    • 检查网络请求,确认 zwplayer.js 正确加载
    • 查看控制台是否有错误信息
    • 使用元素检查器查看 DOM 结构

7.6 性能优化

  1. 延迟加载: 对于非首屏的播放器,使用条件渲染延迟加载
  2. 销毁实例: 组件卸载时播放器实例会被自动清理
  3. 资源预加载: 对于关键视频,可以考虑预加载
  4. CDN 加速: 将 zwplayer 核心库部署到 CDN,使用 zwplayerlib 属性指定

8. 更新日志

  • v1.0.5: 支持 React 19,新增路由安全切换
  • v1.0.4: 新增缩略图、章节、弹幕等功能
  • v1.0.2: 优化 StrictMode 兼容性
  • v1.0.1: 初始版本,支持基本播放功能

9. 相关链接