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/ed/ed_hd.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/ed/ed_hd.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/ed/ed_hd.mp4';
  const poster = 'https://cdn.zwplayer.cn/media/ed/ed_hd_poster.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/ed/ed_hd.mp4';

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

  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/ed/ed_hd.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 媒体地址参数(可以是 string、object、array 类型)。如果此属性在运行中发生变动,则组件会自动调用 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 视频元素属性对象,可以包含 url 和 videostyle 属性。 新增
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: 媒体地址(string、object 或 array)
  • 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. 相关链接