{"_id":"@cqsjjb/web-video-player","_rev":"7-454b50f7194852440027a552d53698b5","name":"@cqsjjb/web-video-player","dist-tags":{"latest":"1.0.0"},"versions":{"0.0.1-alpha.0":{"name":"@cqsjjb/web-video-player","version":"0.0.1-alpha.0","author":{"name":"jiaoxiwei"},"license":"MIT","_id":"@cqsjjb/web-video-player@0.0.1-alpha.0","maintainers":[{"name":"cqjjb","email":"995934055@qq.com"}],"dist":{"shasum":"35d698e35f4cc0986eedee3fd48f0718ff0b4ce3","tarball":"https://registry.npmjs.org/@cqsjjb/web-video-player/-/web-video-player-0.0.1-alpha.0.tgz","fileCount":5,"integrity":"sha512-A4LXshMTS2W5Atrlcn8rcDH3NBgxtuPwyCgOllocagtM9U9BCQHgmjwX3Q3J0Mg2HnCFlW4wiLyYRgVSzlKJWg==","signatures":[{"sig":"MEYCIQDmZ0ofMg1pZWGiT4gNMoVArB1PBOqipp+2RjGmc3xmHQIhAKMNCdMi0gttreEnQ5J9Wqgm0V266UF+6ErNskcDjZt1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":23393},"main":"index.js","types":"./index.d.ts","gitHead":"09ce4e03801a20093b1bc78f3142343e5a4fa02b","_npmUser":{"name":"cqjjb","email":"995934055@qq.com"},"_npmVersion":"10.9.0","description":"Web视频播放器","directories":{},"_nodeVersion":"22.12.0","dependencies":{"video.js":"latest"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/web-video-player_0.0.1-alpha.0_1758711486071_0.24253980180855272","host":"s3://npm-registry-packages-npm-production"}},"0.0.1-alpha.1":{"name":"@cqsjjb/web-video-player","version":"0.0.1-alpha.1","author":{"name":"jiaoxiwei"},"license":"MIT","_id":"@cqsjjb/web-video-player@0.0.1-alpha.1","maintainers":[{"name":"cqjjb","email":"995934055@qq.com"}],"dist":{"shasum":"aaa00dd7da17640df6abedf62ef1fb869235f1a9","tarball":"https://registry.npmjs.org/@cqsjjb/web-video-player/-/web-video-player-0.0.1-alpha.1.tgz","fileCount":5,"integrity":"sha512-N9UNePDx/9zUsuzdhbhRHn3IQxMWyysqDNb6hF6Kpu5HiDduN4Rulz3qtrj9Nn64QcW6bze3Q/3uciEbPLYMtA==","signatures":[{"sig":"MEUCIQD3PaidSFZbRAgMEiOvzXwwvVdevuesJ5MprVz+pEyKSQIgZYIfxc3yTi6t7v60FDI7vbDPYmbv+fLspE5vku2FKIc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180549},"main":"index.js","types":"./index.d.ts","gitHead":"6e8f2277f93cead412d186b356025cb67c5cced6","_npmUser":{"name":"cqjjb","email":"995934055@qq.com"},"_npmVersion":"10.9.0","description":"Web视频播放器","directories":{},"_nodeVersion":"22.12.0","dependencies":{"video.js":"latest"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/web-video-player_0.0.1-alpha.1_1760773348872_0.8207554761243467","host":"s3://npm-registry-packages-npm-production"}},"0.0.1-alpha.3":{"name":"@cqsjjb/web-video-player","version":"0.0.1-alpha.3","author":{"name":"jiaoxiwei"},"license":"MIT","_id":"@cqsjjb/web-video-player@0.0.1-alpha.3","maintainers":[{"name":"cqjjb","email":"995934055@qq.com"}],"dist":{"shasum":"4b5f02e2abb32c0591ad25371565ddd88c46676d","tarball":"https://registry.npmjs.org/@cqsjjb/web-video-player/-/web-video-player-0.0.1-alpha.3.tgz","fileCount":5,"integrity":"sha512-kKvHjR6InOlIdFvgH8xRKwxTKo2bLKFyvR7oHjNdKczsEAM1fHhvnJTXrut0nhkXQQzehBRmjYARE9Tcx/YCkA==","signatures":[{"sig":"MEUCIBUBY/8akIsgWnUqXEAUwA2KKqxeqAXCabgsXmIZCRGQAiEAxM9GAvuUQvfxTHgjKm2zA4IEW8iZ0UsD1Xn47xneZtU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":35307},"main":"index.js","types":"./index.d.ts","gitHead":"6e8f2277f93cead412d186b356025cb67c5cced6","_npmUser":{"name":"cqjjb","email":"995934055@qq.com"},"_npmVersion":"10.9.0","description":"Web视频播放器","directories":{},"_nodeVersion":"22.12.0","dependencies":{"video.js":"latest"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/web-video-player_0.0.1-alpha.3_1760774228118_0.8660986541585001","host":"s3://npm-registry-packages-npm-production"}},"0.0.1-alpha.4":{"name":"@cqsjjb/web-video-player","version":"0.0.1-alpha.4","author":{"name":"jiaoxiwei"},"license":"MIT","_id":"@cqsjjb/web-video-player@0.0.1-alpha.4","maintainers":[{"name":"cqjjb","email":"995934055@qq.com"}],"dist":{"shasum":"2b8b5a31f4b7e465b99ecbcd89007368e5690d16","tarball":"https://registry.npmjs.org/@cqsjjb/web-video-player/-/web-video-player-0.0.1-alpha.4.tgz","fileCount":5,"integrity":"sha512-iyQQHX7tQxH+gOmXEEc8m8xHiKrupoXYPaKTucj9gV6AyrlpLrmZH4pLL/BRMbv0FArUPI8EstdbqVQAlJ4XZA==","signatures":[{"sig":"MEUCIE0NjsBg6XexNIIHT2s3hg1APNcREvP1PUBBQy0S7hkIAiEA4lk7QEagGLtF/8Rt8SWG7KCyN4B9uOgCN+D572+NV9A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":40591},"main":"index.js","types":"./index.d.ts","gitHead":"fe7604eaaa49dafe50cfa25bf2d4fb758297b2af","_npmUser":{"name":"cqjjb","email":"995934055@qq.com"},"_npmVersion":"10.9.0","description":"Web视频播放器","directories":{},"_nodeVersion":"22.12.0","dependencies":{"video.js":"latest"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/web-video-player_0.0.1-alpha.4_1763026555815_0.5249090929723452","host":"s3://npm-registry-packages-npm-production"}},"0.0.1-alpha.5":{"name":"@cqsjjb/web-video-player","version":"0.0.1-alpha.5","author":{"name":"jiaoxiwei"},"license":"MIT","_id":"@cqsjjb/web-video-player@0.0.1-alpha.5","maintainers":[{"name":"cqjjb","email":"995934055@qq.com"}],"dist":{"shasum":"16d08b4b298f9fe935b96dc809621556e2be974a","tarball":"https://registry.npmjs.org/@cqsjjb/web-video-player/-/web-video-player-0.0.1-alpha.5.tgz","fileCount":5,"integrity":"sha512-8s7mryIDVxBRcWxd1lqp6s5q94whKYkLoNpNVFU5KritRppEf2KXaFpxo7p1PPrJW1PKmZYGGhyAHROaj3yGPA==","signatures":[{"sig":"MEUCIQDhEyKCZzckgUZeY/2C1NHmS9lGJhRiowqYku5CVSm7bQIgTNwMTabzCoPLKj9URK+r7ce2699zQDPmGN81tWzQSvM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":41139},"main":"index.js","types":"./index.d.ts","gitHead":"fe7604eaaa49dafe50cfa25bf2d4fb758297b2af","_npmUser":{"name":"cqjjb","email":"995934055@qq.com"},"_npmVersion":"10.9.0","description":"Web视频播放器","directories":{},"_nodeVersion":"22.12.0","dependencies":{"video.js":"latest"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/web-video-player_0.0.1-alpha.5_1763032614804_0.7469810435447666","host":"s3://npm-registry-packages-npm-production"}},"0.0.1-alpha.6":{"name":"@cqsjjb/web-video-player","version":"0.0.1-alpha.6","author":{"name":"jiaoxiwei"},"license":"MIT","_id":"@cqsjjb/web-video-player@0.0.1-alpha.6","maintainers":[{"name":"cqjjb","email":"995934055@qq.com"}],"dist":{"shasum":"67d6692fb25289f728b36dee6aecd5b242637f8d","tarball":"https://registry.npmjs.org/@cqsjjb/web-video-player/-/web-video-player-0.0.1-alpha.6.tgz","fileCount":5,"integrity":"sha512-2OTBlQZ2A003Z7hGWzpc1qHCGPzqVoD14zxuhSpCHyyWWsPC0AQdZZHYomTLpYBKt6iYWqKfceUowu7AayR0FA==","signatures":[{"sig":"MEYCIQC7b6m5OwKxRxMR8hPEz7sS9GQ5qD0zWmD8bWKjhbM5RwIhAPGGSI+xjQoo9iWVkHEfXLoU+G6ZrXbCKXLiEx9n0Kxi","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":41254},"main":"index.js","types":"./index.d.ts","gitHead":"82175563b5c5273b260eeb6a4702fb86e8dabea5","_npmUser":{"name":"cqjjb","email":"995934055@qq.com"},"_npmVersion":"10.9.0","description":"Web视频播放器","directories":{},"_nodeVersion":"22.12.0","dependencies":{"video.js":"latest"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/web-video-player_0.0.1-alpha.6_1763041605347_0.6597719551749188","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@cqsjjb/web-video-player","version":"1.0.0","description":"基于 video.js 封装的 React 视频播放器组件，专为网络课程教育应用设计","main":"index.js","types":"index.d.ts","keywords":["react","video","player","videojs","video.js","react-video-player","web-video-player","education","course"],"author":{"name":"1042656836@qq.com"},"license":"MIT","publishConfig":{"access":"public"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"dependencies":{"video.js":"^8.6.1"},"_id":"@cqsjjb/web-video-player@1.0.0","gitHead":"85dd43f0c43b87df17387e0b4c13b7d57b742f1c","_nodeVersion":"22.12.0","_npmVersion":"10.9.0","dist":{"integrity":"sha512-7xJekzy+CeQXUoYfNc1sQoz60aY8lufsFU6+y2XnoVTLAKG3uHy/vTf1gTwQeb3W22LxEJ+L25I+0t2dtTBdBw==","shasum":"9e4508ec9b95031d6bd366518b2a49dcf52f4813","tarball":"https://registry.npmjs.org/@cqsjjb/web-video-player/-/web-video-player-1.0.0.tgz","fileCount":6,"unpackedSize":42034,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFCcQ8Y220XBLUhwQ3E3LRbNVzBazKinno2jJL4YvGd2AiEAzmoQU2F1NIehwu6GDpy9bphlhwFgNm8njvdcy0cUxjU="}]},"_npmUser":{"name":"cqjjb","email":"995934055@qq.com"},"directories":{},"maintainers":[{"name":"cqjjb","email":"995934055@qq.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/web-video-player_1.0.0_1763109438261_0.042430472073420544"},"_hasShrinkwrap":false}},"time":{"created":"2025-09-24T10:58:05.979Z","modified":"2025-11-14T08:37:18.630Z","0.0.1-alpha.0":"2025-09-24T10:58:06.261Z","0.0.1-alpha.1":"2025-10-18T07:42:29.088Z","0.0.1-alpha.3":"2025-10-18T07:57:08.332Z","0.0.1-alpha.4":"2025-11-13T09:35:56.040Z","0.0.1-alpha.5":"2025-11-13T11:16:54.976Z","0.0.1-alpha.6":"2025-11-13T13:46:45.540Z","1.0.0":"2025-11-14T08:37:18.435Z"},"author":{"name":"1042656836@qq.com"},"license":"MIT","description":"基于 video.js 封装的 React 视频播放器组件，专为网络课程教育应用设计","maintainers":[{"name":"cqjjb","email":"995934055@qq.com"}],"readme":"# @cqsjjb/web-video-player\n\n`@cqsjjb/web-video-player` 是一个基于 [video.js](https://videojs.com/) 封装的 React 视频播放器组件，专为网络课程教育应用设计，提供丰富的视频播放功能和高度可定制的用户体验。\n\n## 特性\n\n- **多清晰度支持** - 支持多视频源切换，自动清晰度选择\n- **智能进度条** - 自定义双层进度条，支持已观看/未观看区域限制\n- **试看功能** - 内置试看时间限制，支持付费内容预览\n- **水印支持** - 可自定义水印文本和样式\n- **字幕轨道** - 支持多语言字幕和音轨切换\n- **播放控制** - 丰富的播放控制选项（速率、画中画、全屏等）\n- **权限控制** - 细粒度的功能控制（下载、快进、音量等）\n- **TypeScript 支持** - 完整的类型定义\n\n## 安装\n\n```bash\nnpm install @cqsjjb/web-video-player\n# 或\nyarn add @cqsjjb/web-video-player\n# 或\npnpm add @cqsjjb/web-video-player\n```\n\n## 🚀 快速开始\n\n```tsx\nimport React from 'react';\nimport WebVideoPlayer from '@cqsjjb/web-video-player';\n\nfunction App() {\n  return (\n    <WebVideoPlayer\n      width=\"800\"\n      sources={[\n        { src: 'https://example.com/video.mp4', label: '高清', default: true }\n      ]}\n      poster=\"https://example.com/poster.jpg\"\n    />\n  );\n}\n```\n\n## API 文档\n\n### WebVideoPlayerProps\n\n| 属性 | 类型 | 默认值 | 说明 |\n|------|------|-------|------|\n| `width` | `number \\| string` | `520` | 播放器宽度，数字(px)或百分比 |\n| `sources` | `VideoSource[]` | `[]` | 视频源列表，支持多清晰度切换 |\n| `poster` | `string` | `undefined` | 视频封面图 URL |\n| `tracks` | `VideoTrack[]` | `[]` | 字幕轨道列表 |\n| `fluid` | `boolean` | `true` | 是否自适应父容器宽度 |\n| `preload` | `'auto' \\| 'metadata' \\| 'none'` | `'auto'` | 视频预加载策略 |\n| `controls` | `boolean` | `true` | 是否显示默认控制栏 |\n| `autoplay` | `boolean` | `false` | 是否自动播放 |\n| `allowFullscreen` | `boolean` | `true` | 是否允许全屏 |\n| `allowDownload` | `boolean` | `true` | 是否显示下载按钮 |\n| `trialTime` | `number` | `undefined` | 试看时长（秒），超过暂停并回到起始位置 |\n| `watermark` | `string` | `undefined` | 视频水印文本 |\n| `watermarkStyle` | `WatermarkStyle` | `{}` | 水印样式配置 |\n| `subtitleStyle` | `React.CSSProperties` | `undefined` | 字幕容器样式，作用于 `.vjs-text-track-display` |\n| `subtitleClassName` | `string` | `undefined` | 附加到 `.vjs-text-track-display` 的类名，便于自定义样式 |\n| `disableSeek` | `boolean` | `false` | 是否禁用快进 |\n| `enablePiP` | `boolean` | `false` | 是否启用画中画 |\n| `disableVolume` | `boolean` | `false` | 是否隐藏音量控制 |\n| `seekableWatchedOnly` | `boolean` | `false` | 是否仅允许在已观看区域拖动进度 |\n| `muted` | `boolean` | `false` | 是否默认静音 |\n| `loop` | `boolean` | `false` | 是否循环播放 |\n| `mini` | `boolean` | `false` | 是否启用迷你模式（非全屏仅显示播放/暂停、全屏按钮） |\n| `rootStyle` | `React.CSSProperties` | `{}` | 播放器根元素样式 |\n| `playbackRates` | `number[]` | `[]` | 播放速率选项 |\n| `enablePlaybackRate` | `boolean` | `true` | 是否启用播放速率控制 |\n| `showDuration` | `boolean` | `true` | 是否显示时长信息 |\n| `onTrialEnded` | `() => void` | `undefined` | 试看结束回调 |\n| `onError` | `(error?: any) => void` | `undefined` | 播放错误回调 |\n\n### VideoSource\n\n```ts\ninterface VideoSource {\n  /** 视频地址 */\n  src: string;\n  /** 显示名称，例如 \"高清\"、\"标清\" */\n  label: string;\n  /** 视频类型，如 \"video/mp4\"，可选，会自动检测 */\n  type?: string;\n  /** 是否为默认播放源，默认 false */\n  default?: boolean;\n}\n```\n\n### VideoTrack\n\n```ts\ninterface VideoTrack extends React.TrackHTMLAttributes<HTMLTrackElement> {}\n```\n\n### WatermarkStyle\n\n```ts\ninterface WatermarkStyle extends React.CSSProperties {\n  /** 水印位置，默认 'top-left' */\n  position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n  /** 水印透明度，默认 0.3 */\n  opacity?: number;\n  /** 水印字体大小，默认 14 */\n  fontSize?: number;\n  /** 水印颜色，默认 '#fff' */\n  color?: string;\n}\n```\n\n### WebVideoPlayerRef\n\n```ts\ninterface WebVideoPlayerRef {\n  /** 获取 video.js player 实例 */\n  getPlayer: () => videojs.Player | null;\n  /** 获取原生 HTMLVideoElement */\n  getNativePlayer: () => HTMLVideoElement | null;\n}\n```\n\n## 使用示例\n\n### 基础用法\n\n```tsx\nimport React from 'react';\nimport WebVideoPlayer from '@cqsjjb/web-video-player';\n\nfunction BasicExample() {\n  return (\n    <WebVideoPlayer\n      width=\"800\"\n      sources={[\n        { src: 'https://example.com/video.mp4', label: '高清', default: true }\n      ]}\n      poster=\"https://example.com/poster.jpg\"\n    />\n  );\n}\n```\n\n### 多清晰度切换\n\n```tsx\nimport React, { useRef } from 'react';\nimport WebVideoPlayer, { WebVideoPlayerRef } from '@cqsjjb/web-video-player';\n\nfunction MultiQualityExample() {\n  const playerRef = useRef<WebVideoPlayerRef>(null);\n\n  const handleGetPlayer = () => {\n    const player = playerRef.current?.getPlayer();\n    console.log('Player instance:', player);\n  };\n\n  return (\n    <div>\n      <WebVideoPlayer\n        ref={playerRef}\n        width=\"100%\"\n        sources={[\n          { src: 'https://example.com/video-480p.mp4', label: '480P', default: true },\n          { src: 'https://example.com/video-720p.mp4', label: '720P' },\n          { src: 'https://example.com/video-1080p.mp4', label: '1080P' }\n        ]}\n        poster=\"https://example.com/poster.jpg\"\n        playbackRates={[0.5, 1, 1.25, 1.5, 2]}\n        onError={(error) => console.error('播放错误:', error)}\n      />\n      <button onClick={handleGetPlayer}>获取播放器实例</button>\n    </div>\n  );\n}\n```\n\n### 试看功能\n\n```tsx\nimport React, { useState } from 'react';\nimport WebVideoPlayer from '@cqsjjb/web-video-player';\n\nfunction TrialExample() {\n  const [trialEnded, setTrialEnded] = useState(false);\n\n  const handleTrialEnded = () => {\n    setTrialEnded(true);\n    alert('试看结束，请购买完整版');\n  };\n\n  return (\n    <div>\n      {trialEnded && (\n        <div style={{ padding: '20px', background: '#f0f0f0', marginBottom: '10px' }}>\n          试看已结束，请购买完整版继续观看\n        </div>\n      )}\n      <WebVideoPlayer\n        width=\"800\"\n        sources={[\n          { src: 'https://example.com/premium-video.mp4', label: '高清', default: true }\n        ]}\n        trialTime={60} // 60秒试看\n        watermark=\"试看版本\"\n        watermarkStyle={{\n          position: 'bottom-right',\n          opacity: 0.5,\n          fontSize: 16,\n          color: '#ff0000'\n        }}\n        onTrialEnded={handleTrialEnded}\n      />\n    </div>\n  );\n}\n```\n\n### 字幕支持\n\n```tsx\nimport React from 'react';\nimport WebVideoPlayer from '@cqsjjb/web-video-player';\n\nfunction SubtitleExample() {\n  return (\n    <WebVideoPlayer\n      width=\"800\"\n      sources={[\n        { src: 'https://example.com/video.mp4', label: '高清', default: true }\n      ]}\n      tracks={[\n        {\n          kind: 'subtitles',\n          src: 'https://example.com/subtitles-zh.vtt',\n          srcLang: 'zh',\n          label: '中文字幕',\n          default: true\n        },\n        {\n          kind: 'subtitles',\n          src: 'https://example.com/subtitles-en.vtt',\n          srcLang: 'en',\n          label: 'English'\n        }\n      ]}\n    />\n  );\n}\n```\n\n### 字幕样式控制\n\n```tsx\nfunction SubtitleStyleExample() {\n  return (\n    <WebVideoPlayer\n      sources={[\n        { src: 'https://example.com/video.mp4', label: '高清', default: true }\n      ]}\n      tracks={[\n        {\n          kind: 'subtitles',\n          src: 'https://example.com/subtitles-zh.vtt',\n          srcLang: 'zh',\n          label: '中文字幕',\n          default: true\n        }\n      ]}\n      subtitleStyle={{\n        color: '#fff',\n        textShadow: '0 0 4px rgba(0, 0, 0, 0.9)',\n        fontSize: '1.2rem',\n        backgroundColor: 'rgba(0, 0, 0, 0.35)',\n        padding: '4px 10px',\n        borderRadius: '6px'\n      }}\n    />\n  );\n}\n```\n\n### 权限控制\n\n```tsx\nimport React from 'react';\nimport WebVideoPlayer from '@cqsjjb/web-video-player';\n\nfunction PermissionControlExample() {\n  return (\n    <WebVideoPlayer\n      width=\"800\"\n      sources={[\n        { src: 'https://example.com/video.mp4', label: '高清', default: true }\n      ]}\n      // 禁用下载和右键菜单\n      allowDownload={false}\n      // 禁用快进\n      disableSeek={true}\n      // 禁用音量控制\n      disableVolume={true}\n      // 只能在已观看区域快进\n      seekableWatchedOnly={true}\n      // 禁用画中画\n      enablePiP={false}\n    />\n  );\n}\n```\n\n### 自定义样式\n\n```tsx\nimport React from 'react';\nimport WebVideoPlayer from '@cqsjjb/web-video-player';\n\nfunction CustomStyleExample() {\n  return (\n    <WebVideoPlayer\n      width=\"100%\"\n      sources={[\n        { src: 'https://example.com/video.mp4', label: '高清', default: true }\n      ]}\n      rootStyle={{\n        borderRadius: '8px',\n        overflow: 'hidden',\n        boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)'\n      }}\n      watermark=\"自定义水印\"\n      watermarkStyle={{\n        position: 'bottom-left',\n        opacity: 0.3,\n        fontSize: 14,\n        color: '#ffffff',\n        textShadow: '1px 1px 2px rgba(0, 0, 0, 0.5)'\n      }}\n    />\n  );\n}\n```\n\n## 最佳实践\n\n### 1. 性能优化\n\n```tsx\n// 使用 useMemo 优化 sources 数组\nconst sources = useMemo(() => [\n  { src: videoUrl, label: '高清', default: true }\n], [videoUrl]);\n\n// 使用 useCallback 优化回调函数\nconst handleError = useCallback((error) => {\n  console.error('播放错误:', error);\n}, []);\n```\n\n### 2. 错误处理\n\n```tsx\nconst [error, setError] = useState(null);\n\n<WebVideoPlayer\n  sources={sources}\n  onError={(err) => {\n    setError(err);\n    // 可以显示错误提示或重试按钮\n  }}\n/>\n\n{error && (\n  <div className=\"error-message\">\n    视频加载失败，请检查网络连接\n  </div>\n)}\n```\n\n### 3. 响应式设计\n\n```tsx\n// 使用 CSS 媒体查询或 JavaScript 动态调整\nconst [playerWidth, setPlayerWidth] = useState('100%');\n\nuseEffect(() => {\n  const updateWidth = () => {\n    if (window.innerWidth < 768) {\n      setPlayerWidth('100%');\n    } else {\n      setPlayerWidth('800px');\n    }\n  };\n  \n  updateWidth();\n  window.addEventListener('resize', updateWidth);\n  return () => window.removeEventListener('resize', updateWidth);\n}, []);\n```\n\n## License\n\nMIT License - 详见 [LICENSE](LICENSE) 文件\n\n## 版权信息\n\n版权所有：重庆市建教帮科技有限责任公司\n\n作者：1042656836@qq.com\n","readmeFilename":"README.md","keywords":["react","video","player","videojs","video.js","react-video-player","web-video-player","education","course"]}