Semi Design VideoPlayer 视频播放器组件实战指南:从基础播放到清晰度切换与原生能力控制

发布时间:2026/9/24 15:59:00
Semi Design VideoPlayer 视频播放器组件实战指南:从基础播放到清晰度切换与原生能力控制 Semi Design VideoPlayer 视频播放器组件实战指南从基础播放到清晰度切换与原生能力控制【免费下载链接】semi-designA modern, comprehensive, flexible design system and React UI library, AI-friendly built-in.Provide 3000 Design Tokens, easy to build your design system. Make Semi Design to Any Design.‍ Design to Code in one click项目地址: https://gitcode.com/gh_mirrors/se/semi-designSemi Design 的VideoPlayerdouyinfe/semi-ui是一个开箱即用的 React 视频播放器组件本文围绕其在当前仓库中的官方文档content/plus/videoPlayer/index-en-US.md中文版见 content/plus/videoPlayer/index.md展开逐一讲解从引入、基础播放、菜单栏定制、倍速/音量/清晰度/线路切换、章节标记到通过 ref 操控原生video元素的完整用法并结合仓库源码揭示其底层实现原理Foundation 状态机、进度条分区渲染、键盘快捷键、全屏滚动位置恢复等帮助你在业务中快速落地一个功能完备的播放器。快速开始引入与基本用法VideoPlayer与 Semi Design 其他组件一样直接从douyinfe/semi-ui引入即可组件在 packages/semi-ui/index.ts 中被统一导出实现位于 packages/semi-ui/videoPlayer/index.tsximport { VideoPlayer } from douyinfe/semi-ui;基本使用只需两个核心属性通过src传入视频地址通过poster传入视频封面地址再配合height指定高度import React from react; import { VideoPlayer } from douyinfe/semi-ui; () { const src https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4; const poster https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg; return ( VideoPlayer height{630} src{src} poster{poster} / ); };组件内部渲染的是原生video元素controls{false}由 Semi 自绘控件层因此浏览器原生的视频格式支持范围MP4/WebM 等都直接适用。从实现看packages/semi-ui/videoPlayer/index.tsx它还额外挂载了一个track kindcaptions用于字幕与captionsSrc属性对应。定制菜单栏控件controlsList播放器底部菜单栏默认展示全部控件可通过controlsList按需裁剪展示项。它接受一个字符串数组可选的控件标识与默认值如下控件标识含义play播放 / 暂停next重新播放Restart 图标time当前时间 / 总时长volume音量Popover 悬浮滑杆playbackRate倍速选择quality清晰度选择route线路选择mirror镜像翻转fullscreen全屏pictureInPicture画中画默认值完整数组[play, next, time, volume, playbackRate, quality, route, mirror, fullscreen, pictureInPicture]例如只保留播放、时间、音量、倍速和全屏import React from react; import { VideoPlayer } from douyinfe/semi-ui; () { const src https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4; const poster https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg; const controlsList [play, time, volume, playbackRate, fullscreen]; return ( VideoPlayer height{630} src{src} poster{poster} controlsList{controlsList} / ); };从源码看菜单栏渲染时的显隐判断由 Foundation 的shouldShowControlItem(name)完成packages/semi-foundation/videoPlayer/foundation.ts即判断该名称是否存在于controlsList中对应的控件名称常量定义在 packages/semi-foundation/videoPlayer/constants.ts。因此传入任何不在上表内的字符串都不会渲染对应控件。循环播放与快进快退循环播放通过loop开启循环播放直接透传给原生video的loop属性import React from react; import { VideoPlayer } from douyinfe/semi-ui; () { const src https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4; const poster https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg; return ( VideoPlayer height{630} src{src} poster{poster} loop{true} / ); };快进快退seekTime用于设定快进快退的时间跨度单位秒默认 10 秒。除了在进度条上点击/拖拽跳转外还可以通过键盘左右方向键快进快退。下面的示例把seekTime与Select联动让用户自由选择 5s / 10s / 15simport React from react; import { VideoPlayer, Select } from douyinfe/semi-ui; () { const [seekTime, setSeekTime] useState(5); const src https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4; const poster https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg; return ( div span style{{ marginBottom: 10 }}Please select the fast forward and rewind time/span Select value{seekTime} style{{ width: 100, marginLeft: 10 }} onChange{(value) setSeekTime(value)} optionList{[ { label: 5s, value: 5 }, { label: 10s, value: 10 }, { label: 15s, value: 15 }, ]} placeholderPlease select the fast forward and rewind time / VideoPlayer height{630} style{{ marginTop: 10 }} src{src} poster{poster} seekTime{seekTime} / /div ); };键盘交互的底层实现在 packages/semi-foundation/videoPlayer/foundation.ts空格键触发播放/暂停ArrowLeft/ArrowRight分别执行currentTime - seekTime/currentTime seekTime。值得注意的细节是只有当焦点位于播放器容器内部时键盘事件才会生效通过videoWrapper.contains(document.activeElement)判断避免与其他页面的可交互元素产生按键冲突。默认seekTime为 10numbers.DEFAULT_SEEK_TIME见 packages/semi-foundation/videoPlayer/constants.ts。播放速率playbackRateList 与 defaultPlaybackRate通过playbackRateList自定义倍速选择列表每一项为{ label, value }结构import React from react; import { VideoPlayer } from douyinfe/semi-ui; () { const playbackRateList [ { label: 0.5x, value: 0.5 }, { label: 1.0x, value: 1 }, { label: 1.5x, value: 1.5 }, { label: 2.0x, value: 2 }, ]; const src https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4; const poster https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg; return ( VideoPlayer height{630} src{src} poster{poster} playbackRateList{playbackRateList} / ); };文档 API 表说明不传playbackRateList时默认展示 6 种播放速率——0.5、0.75、1.0、1.25、1.5 和 2.0。需要补充的是查看当前仓库的 packages/semi-foundation/videoPlayer/constants.ts代码中实际声明的DEFAULT_PLAYBACK_RATE数组目前包含 5 项2.0x、1.5x、1.25x、1.0x、0.75x注意暂未包含 0.5x如果你的业务对默认倍速列表有强约束建议显式传入playbackRateList以保证行为可控。切换倍速时组件会调用原生video.playbackRate并通过onRateChange(rate: number)回调通知外部packages/semi-foundation/videoPlayer/foundation.ts同时右上角会弹出短暂的通知提示如 播放速度已切换为 2.0x。音量控制volume 与 mutedvolume用于设置初始音量取值范围 0 - 100默认 100设置muted为true则静音播放。二者都支持受控/非受控使用import React from react; import { VideoPlayer } from douyinfe/semi-ui; () { const src https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4; const poster https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg; return ( VideoPlayer height{630} src{src} poster{poster} muted{true} / ); };音量调整的底层逻辑见 packages/semi-foundation/videoPlayer/foundation.ts组件把 0-100 的整数音量换算为原生的 0-1 浮点值video.volume volume / 100音量被调为 0 时自动将muted置为true点击音量图标则执行静音/取消静音的切换取消静音时会恢复静音前的音量值。音量菜单中的竖向滑杆复用自AudioSlideraudioPlayer 组件悬停音量图标即可弹出packages/semi-ui/videoPlayer/index.tsx。清晰度与线路切换qualityList / routeList播放器原生支持多清晰度与多线路切换二者机制完全对称清晰度qualityList设置清晰度列表defaultQuality设置初始清晰度onQualityChange回调中自行更新src线路routeList设置线路列表defaultRoute设置初始线路onRouteChange回调中自行更新src。import React from react; import { VideoPlayer } from douyinfe/semi-ui; () { const [src, setSrc] useState(https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4); const playList [ { src: https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4, quality: 1080p, }, { src: https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/video/vchart-show-video-480p.mp4, quality: 480p, }, ]; const poster https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg; const updateVideoSource (quality) { const source playList.find((item) item.quality quality); setSrc(source.src); }; return ( VideoPlayer height{630} src{src} poster{poster} defaultQuality{1080p} qualityList{[ { label: 1080p, value: 1080p }, { label: 480p, value: 480p }, ]} onQualityChange{(quality) { console.log(quality change, quality); updateVideoSource(quality); }} / ); };这里有一个对用户体验很关键的实现细节切换清晰度/线路后视频地址src变化会导致原生视频重新加载。组件通过restorePlayPosition()packages/semi-foundation/videoPlayer/foundation.ts在loadeddata事件中自动恢复切换前的播放位置并且如果切换前正处于播放状态会继续保持播放——避免用户切清晰度后从头再看一遍。同时getDerivedStateFromProps会同步外部传入的src变化到内部 statepackages/semi-ui/videoPlayer/index.tsx。章节标记markersmarkers允许在进度条上划分多个章节区间每项包含start起始时间点秒和title章节标题import React from react; import { VideoPlayer } from douyinfe/semi-ui; () { const src https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4; const poster https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg; const markers [ { start: 0, title: Start }, { start: 4, title: Function Introduction }, { start: 38, title: Figma Plugin }, { start: 51, title: Ending } ]; return ( VideoPlayer height{630} src{src} poster{poster} markers{markers} / ); };markers的渲染逻辑位于进度条组件 packages/semi-ui/videoPlayer/videoProgress.tsx组件会把相邻标记之间的区间切分成独立的分段MarkerListItem每个分段按start / max * 100%计算left、按(end - start) / max * 100%计算width铺满整条进度条。这样带来的能力是已播放进度、缓冲进度按分段分别着色视觉上呈分段填充效果getPlayedWidth/getLoadedWidth见 packages/semi-foundation/videoPlayer/progressFoundation.ts悬停进度条时Tooltip 会同时展示当前所在章节标题 对应时间点packages/semi-ui/videoPlayer/videoProgress.tsx点击/拖拽跳转时进度条 handle 会自动归属到所在章节分段。Marker数据结构定义在 packages/semi-foundation/videoPlayer/progressFoundation.ts包含start: number与title: string两个字段。不传markers时进度条退化为单分段的普通进度条。主题themetheme用于切换播放器主题可选dark默认与light注意它只影响播放器的背景色不影响控件排布import React from react; import { VideoPlayer } from douyinfe/semi-ui; () { const src https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4; const poster https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg; return ( VideoPlayer height{630} src{src} poster{poster} theme{light} / ); };从源码看theme被拼接为semi-videoPlayer-wrapper-dark/light这样的修饰类作用于视频外层容器packages/semi-ui/videoPlayer/index.tsx对应的 SCSS 变量定义在 packages/semi-foundation/videoPlayer/videoPlayer.scss 与 variables.scss 中你可以像定制其他 Semi 组件一样通过 Design Token 调整相关背景色。通过 ref 获取原生 video 元素VideoPlayer支持ref/forwardRef可以直接拿到原生video元素实现比组件自带控件更灵活的控制例如多个视频的同步播放/暂停import React, { useRef } from react; import { VideoPlayer, Button } from douyinfe/semi-ui; () { const videoRef1 useRef(); const videoRef2 useRef(); const src https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4; const poster https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg; const handlePlayAll () { const v1 videoRef1.current; const v2 videoRef2.current; if (v1) v1.play(); if (v2) v2.play(); }; const handlePauseAll () { const v1 videoRef1.current; const v2 videoRef2.current; if (v1) v1.pause(); if (v2) v2.pause(); }; return ( div div style{{ marginBottom: 12 }} Button onClick{handlePlayAll} style{{ marginRight: 8 }}Play All/Button Button onClick{handlePauseAll}Pause All/Button /div div style{{ display: flex, gap: 12 }} VideoPlayer ref{videoRef1} src{src} poster{poster} height{315} width50% / VideoPlayer ref{videoRef2} src{src} poster{poster} height{315} width50% / /div /div ); };底层实现上组件对外导出的是React.forwardRef包装packages/semi-ui/videoPlayer/index.tsx在getVideoRef()中兼容了函数式 ref 与对象式 ref 两种写法packages/semi-ui/videoPlayer/index.tsx拿到 ref 后即可调用play()、pause()、currentTime、volume、requestPictureInPicture()等任意原生HTMLVideoElementAPI。API 一览VideoPlayer 属性PropertiesDescriptionTypeDefault ValueautoPlayWhether to play automaticallybooleanfalsecaptionsSrcCaptions sourcestring-classNameClass namestring-clickToPlayWhether to enable click to playbooleantruecontrolsListSet the menu bar to display controls. All controls are displayed by default.string[][play, next, time, volume, playbackRate, quality, route, mirror, fullscreen, pictureInPicture]crossOriginThis enum attribute indicates whether CORS is used to fetch the video. CORS-enabled resources can be reused in canvas elements without being polluted. Allowed values are anonymous and use-credentialsanonymous | use-credentials-defaultPlaybackRateDefault playback ratenumber1defaultQualityDefault video resolutionstring-defaultRouteDefault line (route)string-forwardRefPass the ref of the native video element for more flexible controlReact.RefHTMLVideoElement-heightHeightstring | number-loopWhether to enable loop playbackbooleanfalsemarkersChapter markersMarker[]-mutedWhether to play silentlybooleanfalseonPausePause callback() void-onPlayPlay callback() void-onQualityChangeSwitch quality callback(quality: string) void-onRateChangeSwitch rate callback(rate: number) void-onRouteChangeSwitch route callback(route: string) void-onVolumeChangeAdjust volume callback(volume: number) void-playbackRateListRate list. By default 6 playback rates are displayed: 0.5, 0.75, 1.0, 1.25, 1.5 and 2.0Array{ label: string; value: number }-posterPosterstring-qualityListQuality listArray{ label: string; value: string }-routeListRoute listArray{ label: string; value: string }-seekTimeFast forward and rewind time (seconds)number10srcVideo playback addressstring-styleStyleCSSProperties-themeTheme setting, different themes give different background colorsdark | lightdarkvolumeDefault volume (0 - 100)number100widthWidthstring | number-完整 TypeScript 接口定义见 packages/semi-ui/videoPlayer/index.tsx默认值集中在static defaultPropspackages/semi-ui/videoPlayer/index.tsx其中volume、seekTime、defaultPlaybackRate的默认值 100 / 10 / 1 来自 packages/semi-foundation/videoPlayer/constants.ts。MarkerPropertiesDescriptionTypestartStart time point (seconds)numbertitleTitlestring源码级原理补充架构Component Foundation 双层结构与其他 Semi Design 组件一致VideoPlayer采用UI 组件 Foundation 逻辑层的双层架构UI 层 packages/semi-ui/videoPlayer/index.tsx 负责 JSX 渲染与 DOM 事件绑定通过adapter对象把状态更新与回调注入逻辑层逻辑层 packages/semi-foundation/videoPlayer/foundation.ts 承载全部播放控制、事件处理与状态同步。这种解耦让逻辑层可以脱离 React 独立测试与复用。进度条分段着色 Tooltip 章节预览进度条子组件 packages/semi-ui/videoPlayer/videoProgress.tsx 内部同样维护自己的 Foundationpackages/semi-foundation/videoPlayer/progressFoundation.ts负责鼠标拖拽、进度百分比计算限制在 0-1 之间、handle 显隐悬停或拖拽时显示以及文档级mousemove/mouseup监听以实现拖出进度条后仍可继续拖动。已播放与缓冲宽度分别来自currentTime和bufferedValue原生progress事件中取video.buffered.end(...)见 packages/semi-foundation/videoPlayer/foundation.ts。事件与体验细节控制栏自动隐藏鼠标在播放器内移动时显示控制栏静止 3 秒后自动隐藏节流 200ms 处理packages/semi-foundation/videoPlayer/foundation.ts播放中鼠标移出播放器也会隐藏控制栏。全屏滚动位置恢复进入全屏前记录window.scrollX/scrollY退出全屏后恢复兼容 WebKit/Moz/MS 及 iOS SafariwebkitDisplayingFullscreen的 fullscreen APIpackages/semi-foundation/videoPlayer/foundation.ts。画中画调用原生requestPictureInPicture()并通过leavepictureinpicture事件同步播放状态packages/semi-foundation/videoPlayer/foundation.ts。加载/卡顿提示与错误态原生waiting、stalled事件触发加载中/卡顿通知error事件渲染带 ErrorSvg 的错误占位本地化文案来自 packages/semi-ui/locale/source 各语言包的VideoPlayer字段如loading、stall、noResource、videoError等。时间格式化formatTime工具packages/semi-ui/videoPlayer/utils.ts将秒数格式化为mm:ss超过 1 小时自动升级为h:mm:ss。测试与示例仓库中已有针对该组件的端到端测试 cypress/e2e/videoPlayer.spec.js以及 Storybook 演示 packages/semi-ui/videoPlayer/_story/videoPlayer.stories.jsx可作为了解组件交互行为的补充参考。设计变量VideoPlayer的视觉样式由 Design Token 驱动详见文档末尾的DesignToken/区块背景色、进度条、控件配色等均可在 packages/semi-foundation/videoPlayer/variables.scss 中查看默认取值并通过 Semi Design 的主题定制机制覆盖实现与业务品牌一致的外观。小结VideoPlayer是一个零配置可用、按需深度定制的视频播放器组件基础场景只需src poster进阶场景通过controlsList裁剪控件、qualityList/routeList实现多清晰度多线路、markers实现章节导航、ref获取原生元素实现精细操控。结合本文对 foundation.ts、constants.ts、videoProgress.tsx 等源码的剖析你既可以在业务中快速接入也能在遇到复杂定制需求如切换清晰度保持播放位置、全屏状态管理时知其所以然。【免费下载链接】semi-designA modern, comprehensive, flexible design system and React UI library, AI-friendly built-in.Provide 3000 Design Tokens, easy to build your design system. Make Semi Design to Any Design.‍ Design to Code in one click项目地址: https://gitcode.com/gh_mirrors/se/semi-design创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考