更新记录
1.1.6(2026-05-08)
- 修复 iOS 自动首帧封面在播放开始后仍可能遮挡视频画面的问题
1.1.5(2026-05-07)
- 修复试用版发布后组件导入加密协议文件时可能出现的具名导出解析失败
1.1.4(2026-04-24)
- 调整播放器圆角为默认
0,新增borderRadius属性按需配置播放器圆角 - 优化 Android 全屏沉浸式系统栏恢复逻辑,减少退出全屏后状态栏/导航栏状态异常
平台兼容性
uni-app x(5.06)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | √ | √ | - | - |
hans-video
hans-video 是一个面向 uni-app x 的原生视频组件,支持视频播放、封面、全屏、画中画、手势交互和可配置控制层。
支持平台
| 平台 | 支持情况 | 说明 |
|---|---|---|
| Android | 支持 | 建议使用自定义基座或云打包产物验证实际播放与画中画能力 |
| iOS | 支持 | 支持播放、全屏、画中画 |
| Harmony | 不支持 | 组件初始化会触发 1005 unsupported,createHansVideoContext() 返回 null |
接入方式
将插件安装到项目的 uni_modules/hans-video 后,可直接在页面中使用 <hans-video />。
基础示例
<template>
<view class="page">
<hans-video
id="video-player"
class="player"
:source="source"
poster="https://example.com/poster.jpg"
:borderRadius="12"
:controls="true"
:autoplay="false"
@loaded="handleLoaded"
@error="handleError"
@timeupdate="handleTimeUpdate"
/>
</view>
</template>
<script setup lang="uts">
import {
createHansVideoContext,
HansVideoErrorDetail,
HansVideoSource,
HansVideoTimeUpdateEventDetail,
IHansVideoContext
} from '@/uni_modules/hans-video'
const source = ref<HansVideoSource>({
url: 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8',
type: 'hls'
})
let videoContext: IHansVideoContext | null = null
onReady(() => {
nextTick(() => {
videoContext = createHansVideoContext('video-player')
})
})
function handleLoaded(): void {
videoContext?.play()
}
function handleError(event: UniCustomEvent<HansVideoErrorDetail>): void {
console.error('hans-video error', event.detail.code, event.detail.message)
}
function handleTimeUpdate(event: UniCustomEvent<HansVideoTimeUpdateEventDetail>): void {
console.log('progress', event.detail.currentTime, event.detail.duration)
}
</script>
<style>
.page {
padding: 24rpx;
}
.player {
width: 100%;
height: 420rpx;
}
</style>
常用导入
以下公开类型和上下文接口可直接从插件根路径导入,无需引用 utssdk/*:
import {
HansVideoControlButtonArea,
HansVideoControlButtonConfig,
HansVideoControlButtonId,
HansVideoControlIconId,
HansVideoControlIconSource,
HansVideoControlIcons,
HansVideoCustomControlButtonConfig,
HansVideoEpisodeItem,
HansVideoEpisodePanelConfig,
HansVideoSource,
IHansVideoContext,
createHansVideoContext
} from '@/uni_modules/hans-video'
source 参数
source 为对象类型:
type HansVideoSourceHeader = {
name: string
value: string
}
type HansVideoSource = {
url: string
type?: string
headers?: HansVideoSourceHeader[]
cookies?: string[]
}
字段说明:
url:视频地址,必填type:资源类型,可传mp4、hls等标识;不传时由播放器自行判断headers:附加请求头cookies:附加 Cookie 列表,插件会合并为请求头发送
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
source |
HansVideoSource \| null |
null |
视频源 |
autoplay |
boolean |
false |
是否在新视频源加载完成后自动播放 |
muted |
boolean |
false |
是否静音 |
loop |
boolean |
false |
是否循环播放 |
controls |
boolean |
true |
是否显示插件自定义控制层 |
poster |
string |
'' |
封面图地址,支持本地路径、file://、网络地址 |
objectFit |
'contain' \| 'cover' \| 'fill' \| 'stretch' |
'contain' |
视频缩放模式 |
borderRadius |
number |
0 |
播放器圆角,单位 px,默认无圆角 |
playbackRate |
number |
1 |
播放倍速,最小值会被限制为 0.25 |
gestureEnabled |
boolean |
true |
是否启用手势 |
gestureSeekEnabled |
boolean |
true |
是否启用快进快退手势 |
gestureVolumeEnabled |
boolean |
true |
是否启用音量手势 |
gestureBrightnessEnabled |
boolean |
true |
是否启用亮度手势 |
gestureOverlayEnabled |
boolean |
true |
是否显示手势反馈层,仅控制 UI,不关闭手势输入 |
lockControlEnabled |
boolean |
true |
是否显示锁定控制层入口 |
controlButtons |
HansVideoControlButtonConfig[] \| null |
null |
内建控制按钮布局配置,支持区域、顺序、隐藏 |
controlIcons |
HansVideoControlIcons \| null |
null |
内建控制按钮图标配置,支持本地图片路径、file://,Android 也支持 drawable 名 |
customControlButtons |
HansVideoCustomControlButtonConfig[] \| null |
null |
自定义文本按钮列表,仅在全屏控制层显示,点击后只回调不做内置逻辑 |
title |
string |
'' |
全屏态顶部标题 |
episodes |
HansVideoEpisodeItem[] \| null |
null |
选集列表,仅在全屏控制层内显示入口 |
currentEpisodeId |
string |
'' |
当前高亮的剧集 id |
episodeButtonEnabled |
boolean |
true |
是否启用全屏态的选集入口 |
episodePanel |
HansVideoEpisodePanelConfig \| null |
null |
全屏选集面板文案与样式配置 |
HansVideoEpisodeItem 和 HansVideoEpisodePanelConfig
type HansVideoEpisodeItem = {
id: string
label: string
locked?: boolean
badge?: string
}
type HansVideoEpisodePanelConfig = {
title?: string
buttonText?: string
buttonFontFamily?: string
itemLabelMode?: 'label' | 'number'
columns?: number
accentColor?: string
}
title:面板顶部标题buttonText:全屏态选集入口文案buttonFontFamily:入口文案字体族名称,适合覆盖非常见字形itemLabelMode:number时展示数字,label时展示episodes[].labelcolumns:选集网格列数,限制为2-6accentColor:当前高亮集背景色,支持#RRGGBB或#AARRGGBB
示例:
<hans-video
:episodes="episodes"
:episodePanel="{
title: 'Episodes',
buttonText: '剧集',
buttonFontFamily: 'YourLoadedFontFamily',
itemLabelMode: 'number',
columns: 4,
accentColor: '#F6C94C'
}"
/>
controlButtons
type HansVideoControlButtonId =
'playbackRate' |
'episode' |
'fullscreen' |
'lock' |
'cast' |
'prevEpisode' |
'nextEpisode'
type HansVideoControlButtonArea =
'bottom-left' |
'bottom-right' |
'top-right' |
'right-center' |
'hidden'
type HansVideoControlButtonConfig = {
id: HansVideoControlButtonId
area?: HansVideoControlButtonArea
order?: number
visible?: boolean
}
- 支持的内建按钮:
prevEpisode、nextEpisode、playbackRate、episode、fullscreen、cast、lock area支持bottom-left、bottom-right、top-right、right-center、hiddenorder越小越靠前;横向区域按从左到右,纵向区域按从上到下visible: false会隐藏对应按钮;未传时使用默认布局fullscreen会同时影响内联态控制条中的全屏入口;其余扩展按钮仅在全屏控制层显示
示例:
<hans-video
:controlButtons="[
{ id: 'prevEpisode', area: 'bottom-left', order: 10, visible: true },
{ id: 'nextEpisode', area: 'bottom-left', order: 20, visible: true },
{ id: 'playbackRate', area: 'bottom-right', order: 10, visible: true },
{ id: 'episode', area: 'bottom-right', order: 20, visible: true },
{ id: 'fullscreen', area: 'bottom-right', order: 30, visible: true },
{ id: 'cast', area: 'top-right', order: 10, visible: true },
{ id: 'lock', area: 'right-center', order: 10, visible: true }
]"
/>
controlIcons
type HansVideoControlIconId =
'back' |
'play' |
'pause' |
'prevEpisode' |
'nextEpisode' |
'fullscreenEnter' |
'fullscreenExit' |
'cast' |
'lock' |
'unlock'
type HansVideoControlIconSource = {
src: string
}
type HansVideoControlIcons = {
back?: HansVideoControlIconSource
play?: HansVideoControlIconSource
pause?: HansVideoControlIconSource
prevEpisode?: HansVideoControlIconSource
nextEpisode?: HansVideoControlIconSource
fullscreenEnter?: HansVideoControlIconSource
fullscreenExit?: HansVideoControlIconSource
cast?: HansVideoControlIconSource
lock?: HansVideoControlIconSource
unlock?: HansVideoControlIconSource
}
- 只影响内建图标按钮
- 推荐使用项目内本地静态图片路径,例如
/static/hans-video/play.png - 支持
file://路径;Android 额外支持直接传 drawable 资源名 - Android 与 iOS 当前都不支持网络图片 URL
示例:
<hans-video
:controlIcons="{
play: { src: '/static/hans-video/play.png' },
pause: { src: '/static/hans-video/pause.png' },
fullscreenEnter: { src: '/static/hans-video/fullscreen-enter.png' },
fullscreenExit: { src: '/static/hans-video/fullscreen-exit.png' },
cast: { src: '/static/hans-video/cast.png' }
}"
/>
customControlButtons
type HansVideoCustomControlButtonConfig = {
id: string
text: string
fontFamily?: string
area?: HansVideoControlButtonArea
order?: number
visible?: boolean
enabled?: boolean
}
- 与
controlButtons分离;controlButtons只编排内建按钮,customControlButtons只定义业务文本按钮 - 自定义按钮会进入同一套全屏控制层区域排序系统
- 当前仅支持文本按钮,内联态不会显示
enabled: false时按钮保留展示但置灰,点击不会触发事件fontFamily需要传宿主 app 已加载且原生可识别的字体族名称
示例:
<hans-video
:controlButtons="[
{ id: 'episode', area: 'bottom-right', order: 20, visible: true },
{ id: 'fullscreen', area: 'bottom-right', order: 40, visible: true }
]"
:customControlButtons="[
{ id: 'vote', text: '投票', fontFamily: 'YourLoadedFontFamily', area: 'bottom-right', order: 24, visible: true, enabled: true },
{ id: 'share', text: '分享', area: 'bottom-right', order: 28, visible: true, enabled: true }
]"
@customcontrolbuttontap="handleCustomControlButtonTap"
/>
事件
基础事件
| 事件名 | 说明 | 推荐回调类型 |
|---|---|---|
loaded |
资源加载完成 | UniCustomEvent<HansVideoEventDetail> |
play |
开始播放 | UniCustomEvent<HansVideoEventDetail> |
pause |
暂停播放 | UniCustomEvent<HansVideoEventDetail> |
ended |
播放结束 | UniCustomEvent<HansVideoEventDetail> |
waiting |
进入等待状态 | UniCustomEvent<HansVideoEventDetail> |
buffering |
缓冲中 | UniCustomEvent<HansVideoEventDetail> |
error |
播放错误 | UniCustomEvent<HansVideoErrorDetail> |
进度与状态事件
| 事件名 | 说明 | 推荐回调类型 |
|---|---|---|
timeupdate |
播放时间更新 | UniCustomEvent<HansVideoTimeUpdateEventDetail> |
seeking |
开始跳转 | UniCustomEvent<HansVideoSeekEventDetail> |
seeked |
跳转完成 | UniCustomEvent<HansVideoSeekEventDetail> |
fullscreenchange |
全屏状态变化 | UniCustomEvent<HansVideoFullscreenEventDetail> |
pictureinpicturechange |
画中画状态变化 | UniCustomEvent<HansVideoPictureInPictureEventDetail> |
lockchange |
锁定状态变化 | UniCustomEvent<HansVideoLockChangeEventDetail> |
episodeselect |
在全屏选集面板中选择剧集 | UniCustomEvent<HansVideoEpisodeSelectEventDetail> |
casttap |
点击投屏按钮 | UniCustomEvent<HansVideoCastTapEventDetail> |
prevepisodetap |
点击上一集按钮 | UniCustomEvent<HansVideoEpisodeNavTapEventDetail> |
nextepisodetap |
点击下一集按钮 | UniCustomEvent<HansVideoEpisodeNavTapEventDetail> |
customcontrolbuttontap |
点击自定义文本按钮 | UniCustomEvent<HansVideoCustomControlButtonTapEventDetail> |
doubletap |
双击播放器空白区域 | UniCustomEvent<HansVideoDoubleTapEventDetail> |
所有带 payload 的事件都通过 event.detail 读取,例如 event.detail.currentTime、event.detail.message。
doubletap 当前在 Android 与 iOS 的内联播放器空白区域触发,业务侧可根据 event.detail.gestureZone 自行决定播放/暂停、快进或其他逻辑。
手势事件
| 事件名 | 说明 | 推荐回调类型 |
|---|---|---|
gesturestart |
手势开始 | UniCustomEvent<HansVideoGestureEventDetail> |
gesturechange |
手势变更中 | UniCustomEvent<HansVideoGestureEventDetail> |
gestureend |
手势结束 | UniCustomEvent<HansVideoGestureEventDetail> |
HansVideoGestureEventDetail 的字段位于 event.detail:
type:seek、volume、brightnessphase:start、change、endfullscreen:当前是否处于全屏locked:当前是否已锁定控制层gestureZone:left、right、centerdeltaX、deltaY、targetTime、currentTime、duration、volume、brightness:按手势类型返回对应数据
上下文控制
通过 createHansVideoContext(id) 获取播放器上下文:
import { createHansVideoContext } from '@/uni_modules/hans-video'
const videoContext = createHansVideoContext('video-player')
注意事项:
id需要和组件上的id保持一致- 需要在组件完成挂载和原生视图绑定后再获取上下文
- 若返回
null,表示当前原生视图尚未完成绑定,或当前平台不支持该组件
IHansVideoContext 方法
| 方法 | 说明 |
|---|---|
setSource(source) |
运行时切换视频源 |
play() |
播放 |
pause() |
暂停 |
stop() |
停止并回到起点 |
seek(time) |
跳转到指定秒数 |
setPlaybackRate(rate) |
设置播放倍速 |
getCurrentTime() |
获取当前播放时间 |
getDuration() |
获取视频总时长 |
enterFullscreen() |
进入全屏 |
exitFullscreen() |
退出全屏 |
requestPictureInPicture() |
请求进入画中画 |
lockControls() |
锁定控制层 |
unlockControls() |
解除锁定 |
isLocked() |
获取当前是否锁定 |
运行时切换视频源
setSource(source) 使用与组件 source prop 相同的结构,适合切集、切清晰度或切测试源。
import { HansVideoSource } from '@/uni_modules/hans-video'
function switchSource(nextSource: HansVideoSource): void {
videoContext?.setSource(nextSource)
}
错误码
| 错误码 | 说明 |
|---|---|
1001 |
无效的视频源 |
1002 |
视频加载失败 |
1003 |
播放器初始化失败 |
1004 |
跳转失败 |
1005 |
当前平台或宿主环境不支持 |
1999 |
未知错误 |
使用限制
controls控制的是插件自定义控制层,不是系统原生控制条controlButtons只编排内建按钮;如需追加业务动作按钮,使用customControlButtonsepisodes对应的选集入口只在全屏态显示,内联态不会展示autoplay只影响新视频源加载后的自动起播,不替代运行中的play()/pause()控制- 传入
poster时,组件会在起播前和stop()后显示封面;不传时会尝试提取首帧作为预览 cast、prevEpisode、nextEpisode与customControlButtons当前都只派发事件,业务侧需要自行实现投屏、切集或其他动作- Android 全屏横屏和画中画依赖宿主配置;如果宿主工程覆盖了相关清单能力,需要确认最终合并结果仍然保留这些能力
- iOS 画中画需要宿主启用
Background Modes -> Audio, AirPlay, and Picture in Picture

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 348
赞赏 0
下载 11970249
赞赏 1914
赞赏
京公网安备:11010802035340号