VideoPlayer 视频播放器
介绍
- 支持横竖屏布局与组件级全屏(不打断播放进度)
- 长按倍速播放以及长按下拉倍速锁定 (长按触发倍速播放,长按下拉触发倍速锁)
- 沉浸式模式(双指捏合进入/退出,隐藏控件/进度条/弹幕)
- 自定义控制条(按钮显隐、插槽扩展、自定义渲染)
- 进度条预览(分片小窗、时间范围提示、实时时间)
- 拖拽小窗分片预览能力 (拖拽进度条能实时显示当前视频的切片)
- 上次观看记录(10%~90% 区间节流存储,自动恢复并气泡提示)
- 弹幕能力(顶/中/底三栏,密度/速度可配,随倍速/播放状态同步,可动态发送)
- 震动反馈(捏合/长按倍速进入与恢复的轻/重震)
安装与引入
import FQVideoPlayer from '@fq/fq-weapp-ui-pro';
按需样式(按需引入时需要):
import '@fq/fq-weapp-ui-pro/dist/styles/components/video-player.scss';
基础用法
<FQVideoPlayer
loop
autoplay
src="https://ifengqun.oss-cn-shenzhen.aliyuncs.com/uploadtest/0-lxtzk7yj0jm.mp4"
cover="https://applet.ifengqun.com/fq-mall/snapshot/115_first_frame.jpg"
/>
横屏模式 + 组件级全屏
<FQVideoPlayer
src="..."
cover="..."
// 横屏时才展示全屏按钮;进入全屏时 UI 方向切到 Portrait(不影响播放)
controls={{ buttons: { fullScreen: true } }}
/>
进度条预览(小窗 + 时间范围)
<FQVideoPlayer
src="..."
slider={{
size: 'middle',
showTimeRange: true,
enablePreview: true,
previewOptions: [
{ snapshot: 'https://.../output_01.jpg', time: 0 },
{ snapshot: 'https://.../output_02.jpg', time: 14 },
// ...
],
}}
/>
自定义控制条(扩展按钮 / 完全自定义)
const extraButtons = [
<View key="collect" onClick={() => console.log('collect')}>收藏</View>,
<View key="download" onClick={() => console.log('download')}>下载</View>,
];
<FQVideoPlayer
src="..."
controls={{
buttons: { menu: true, play: true, mute: true, rate: true, fullScreen: true },
extraButtons,
// 或者完全自定义:renderButtons: (ctx) => (<View>...</View>)
}}
/>
上次观看(自动记录/恢复)
<FQVideoPlayer
src="..."
lastWatch={{
throttleMs: 4000,
minResumeSec: 5,
ignoreTailSec: 2,
startPercent: 10,
endPercent: 90,
}}
/>
手势与沉浸式
<FQVideoPlayer
src="..."
enablePinchGesture // 默认 true:外捏进入沉浸式,内捏退出
// immersive 也可受控传入(true/false)
/>
弹幕(静态 + 动态发送)
const ref = useRef<any>(null);
<FQVideoPlayer
ref={ref}
src="..."
barrage={{
enabled: true,
density: { top: 2, middle: 3, bottom: 2 },
data: [
{ id: 1, text: 'Hello!', time: 1, zone: 'top' },
{ id: 2, text: '支持倍速/暂停同步', time: 3, zone: 'middle' },
],
}}
/>
// 运行时发送
ref.current?.sendBarrage?.({ text: '这是一条新弹幕' });
API
FQVideoPlayer Props
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
width | number | - | 组件宽(用于方向预判,可选) |
height | number | - | 组件高(用于方向预判,可选) |
src | string | 必填 | 视频地址 |
cover | string | - | 自定义封面(组件层渲染,支持 coverFit) |
coverFit | 'cover' | 'contain' | contain | 封面适配方式 |
muted | boolean | false | 静音 |
controls | boolean | ControlsOptions | true | 控制条:false 隐藏;对象启用自定义按钮与插槽 |
autoplay | boolean | false | 自动播放 |
loop | boolean | false | 循环播放;同时驱动弹幕循环 |
initialTime | number | 0 | 初始起播秒数(不自动播放时仅 seek,不播放) |
objectFit | 'contain' | 'fill' | 'cover' | contain | 视频铺放模式 |
className | string | - | 容器类名 |
styles | FQVideoPlayerStyles | - | 统一样式(root/movableArea/movableView/video/bar/controls/cover) |
barrage | boolean | BarrageOptions | false | 弹幕开关/配置(true 使用默认) |
showSliderRealtimeInfo | boolean | true | 进度条两侧实时时间(左:当前,右:剩余) |
slider | boolean | SliderOptions | true | 进度条开关与配置(对象时需提供 size) |
orientation | Orientation | auto | auto/portrait/landscape(内部自动/强制方向) |
layoutPreset | 'default' | 'compact' | 'spacious' | default | 预留布局预设 |
enableProgressGesture | boolean | false | 是否开启进度滑动手势(保留) |
enablePinchGesture | boolean | true | 是否开启双指捏合沉浸式切换 |
lastWatch | boolean | LastWatchOptions | false | 上次观看:开关/配置 |
onClose | Function: void | - | 关闭按钮回调:竖屏全屏视频场景向外层触发 |
onPlay | Function: void | - | 播放事件 |
onPause | Function: void | - | 暂停事件 |
onEnded | Function: void | - | 结束事件(会清理 lastWatch 缓存) |
onError | (err: ErrorMsg) => void | - | 统一错误(结构化 ErrorMsg) |
onTimeUpdate | Function: void | - | 进度更新 |
onWaiting | Function: void | - | 缓冲中 |
onLoadedMetadata | Function: void | - | 元数据就绪(内部会做方向与初始进度同步) |
onOrientationChange | (o: Orientation) => void | - | 布局方向变化回调 |
onPreloadNextVideo | (id: string) => void | - | 缓冲>10% 时触发预加载下一段 |
onPlayBackRate | (rate: number, type: 'manual' | 'touch') => void | - | 倍速变更回调 |
onScaleChange | (isScale: boolean) => void | - | 缩放进入/退出状态(沉浸式手势) |
onDoubleTap | (e) => void | - | 双击事件 |
videoId | string | - | 自定义视频实例标识(选择器) |
pageId | string | number | - | 页面标识(事件总线隔离) |
ControlsOptions
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
renderLeft | React.ReactNode | 默认关闭按钮 | 左侧区域插槽(竖屏时展示,横屏隐藏)。返回节点替换默认关闭按钮。 |
renderExtraRight | React.ReactNode | - | 最右侧额外区域插槽。 |
buttons | { menu?: boolean; play?: boolean; mute?: boolean; rate?: boolean; fullScreen?: boolean; } | { menu:true, play:true, mute:true, rate:true, fullScreen:false } | 基础按钮显隐配置。全屏按钮需显式开启。 |
extraButtons | React.ReactNode[] | [] | 自定义按钮数组,插入在内置按钮之后,支持直接传节点。非法子类型(对象/函数/布尔)会被安全忽略。 |
renderButtons | `React.ReactNode | (ctx) => React.ReactNode` | - |
子项:buttons
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
menu | boolean | true | 显示“更多/菜单”按钮 |
play | boolean | true | 显示“播放/暂停”按钮 |
mute | boolean | true | 显示“静音/取消静音”按钮 |
rate | boolean | true | 显示“倍速”按钮(循环 0.5x→1x→1.25x→1.5x→2x) |
fullScreen | boolean | false | 显示“全屏”按钮(仅横屏布局生效) |
提示:全屏按钮仅在横屏布局下展示有效(内部已有 isLandscape 判断),且需 fullScreen: true。
SliderOptions
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
size | 'large' | 'middle' | 'small' | 必填 | 进度条尺寸。 |
showRealtimeTimeInfo | boolean | true | 是否在进度条两侧显示实时时间(左:当前,右:剩余)。 |
showTimeRange | boolean | true | 拖拽中是否在顶部显示 time-range 提示文本。 |
enablePreview | boolean | false | 是否启用拖拽预览小窗(若同时使用旧 props enablePreviewSnapshots 也会启用)。 |
previewOptions | { snapshot: string; time: number }[] | - | 预览分片配置,time 为该切片代表的出现秒数。 |
previewThrottleMs | number | 200 | 预览小窗图片更新的节流间隔(毫秒)。 |
styles | { wrapper?; track?; buffer?; handle?; realtime? } | - | 进度条各部件的内联样式定制。 |
说明:横屏时预览小窗按 16:9 呈现,竖屏按 9:16 呈现。
LastWatchOptions
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
throttleMs | number | 4000 | 节流写入本地缓存的最小间隔(毫秒)。 |
minResumeSec | number | 5 | 最小可恢复的播放秒数(不足不记录)。 |
ignoreTailSec | number | 2 | 距离视频结尾的忽略秒数(过近不恢复)。 |
startPercent | number | 10 | 参与记录/恢复的起始百分比(含)。 |
endPercent | number | 90 | 参与记录/恢复的结束百分比(含)。 |
策略:仅在区间内(10%~90%)且间隔到达节流阈值时写入;超出区间或播放结束会清理缓存。
BarrageOptions(弹幕)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled | boolean | false | 是否启用弹幕显示。 |
density | { top?: number; middle?: number; bottom?: number } | - | 顶/中/底三区各自的行数(密度)。 |
speedPxPerSec | number | - | 基础速度(px/s),会随 playbackRate 同步变更。 |
data | BarrageItem[] | - | 静态数据源,BarrageItem 包含 id/text/avatar?/zone?/time。未提供 zone 时内部会稳定分配车道。 |
styles | React.CSSProperties | - | 弹幕容器样式。 |
className | string | - | 弹幕容器自定义类名。 |
BarrageItem 结构:{ id: string|number; text: string; avatar?: string; zone?: 'top'|'middle'|'bottom'; time: number }
Ref 能力(FQVideoPlayerRef)
| 方法 | 签名 | 说明 |
|---|---|---|
play | (): Promise<void> | 播放。 |
pause | (): Promise<void> | 暂停。 |
seek | (time: number): Promise<void> | 跳转到指定秒。 |
setPlaybackRate | (rate: number): Promise<void> | 设置倍速,手动来源会重置锁条与锁定态。 |
mute | (): void | 静音。 |
unmute | (): void | 取消静音。 |
setMuted | (muted: boolean): void | 显式设置静音开关。 |
enterFullscreen | (): void | 组件级全屏(仅横屏布局下进入;已全屏则无效)。 |
exitFullscreen | (): void | 退出组件级全屏。 |
toggleFullscreen | (): void | 切换组件级全屏(横屏进入/退出)。 |
sendBarrage | BarrageItem | 发送一条新弹幕,立即出现在当前时间窗口。 |
getState | (): { playing; loading; muted; playbackRate; immersive; currentTimeSeconds; totalDurationSeconds; bufferPercent; orientation; isFullscreen; } | 获取当前内部状态快照。 |
常见问题
- 全屏按钮为什么不显示?
- 需设置
controls.buttons.fullScreen = true,且仅在横屏布局下显示。
- 需设置
- 预览小窗横屏显示有白边?
- 已在横屏使用 16:9 尺寸,竖屏 9:16;请确保传入的切片图本身也是对应比例。
- 上次观看为何未恢复?
- 小于
startPercent或超过endPercent(或接近结尾ignoreTailSec)不会恢复;首次播放不足minResumeSec也不会记录。
- 小于