更新记录

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 unsupportedcreateHansVideoContext() 返回 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:资源类型,可传 mp4hls 等标识;不传时由播放器自行判断
  • 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 全屏选集面板文案与样式配置

HansVideoEpisodeItemHansVideoEpisodePanelConfig

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:入口文案字体族名称,适合覆盖非常见字形
  • itemLabelModenumber 时展示数字,label 时展示 episodes[].label
  • columns:选集网格列数,限制为 2-6
  • accentColor:当前高亮集背景色,支持 #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
}
  • 支持的内建按钮:prevEpisodenextEpisodeplaybackRateepisodefullscreencastlock
  • area 支持 bottom-leftbottom-righttop-rightright-centerhidden
  • order 越小越靠前;横向区域按从左到右,纵向区域按从上到下
  • 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.currentTimeevent.detail.message

doubletap 当前在 Android 与 iOS 的内联播放器空白区域触发,业务侧可根据 event.detail.gestureZone 自行决定播放/暂停、快进或其他逻辑。

手势事件

事件名 说明 推荐回调类型
gesturestart 手势开始 UniCustomEvent<HansVideoGestureEventDetail>
gesturechange 手势变更中 UniCustomEvent<HansVideoGestureEventDetail>
gestureend 手势结束 UniCustomEvent<HansVideoGestureEventDetail>

HansVideoGestureEventDetail 的字段位于 event.detail

  • typeseekvolumebrightness
  • phasestartchangeend
  • fullscreen:当前是否处于全屏
  • locked:当前是否已锁定控制层
  • gestureZoneleftrightcenter
  • deltaXdeltaYtargetTimecurrentTimedurationvolumebrightness:按手势类型返回对应数据

上下文控制

通过 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 只编排内建按钮;如需追加业务动作按钮,使用 customControlButtons
  • episodes 对应的选集入口只在全屏态显示,内联态不会展示
  • autoplay 只影响新视频源加载后的自动起播,不替代运行中的 play() / pause() 控制
  • 传入 poster 时,组件会在起播前和 stop() 后显示封面;不传时会尝试提取首帧作为预览
  • castprevEpisodenextEpisodecustomControlButtons 当前都只派发事件,业务侧需要自行实现投屏、切集或其他动作
  • Android 全屏横屏和画中画依赖宿主配置;如果宿主工程覆盖了相关清单能力,需要确认最终合并结果仍然保留这些能力
  • iOS 画中画需要宿主启用 Background Modes -> Audio, AirPlay, and Picture in Picture

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率: