更新记录
1.0.0(2026-03-22)
- 支持 Android overlay、iOS in-app float、Android system video PiP 和 iOS out-app PiP。
- 新增
playVideo、pauseVideo、seekVideo视频控制接口。 video-url支持isMuted、allowsExternalPlayback、startPositionMs、autoEnter和aspectRatio。- 新增
appearance.showTitleBar,支持隐藏 overlay / in-app float 的自绘标题栏。
平台兼容性
uni-app(4.87)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | - | - | - | - | √ | √ | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(4.87)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | √ | √ | - | - |
hans-pip
hans-pip 是一个用于 uni-app / uni-app x App 端的小窗口 / 悬浮窗 UTS 插件,统一提供 Android overlay、iOS in-app float 和系统视频 PiP 的管理接口。
安装与导入
统一从插件入口导入,不要直接引用 utssdk/*:
import {
getFloatingWindowManager,
isLogEnabled,
setLogEnabled,
type FloatingWindowOpenOptions,
} from '@/uni_modules/hans-pip'
const windowManager = getFloatingWindowManager()
联调阶段建议开启日志:
setLogEnabled(true)
console.log(`hans-pip log enabled=${isLogEnabled()}`)
支持范围
| mode | Android | iOS | Harmony | contentType |
|---|---|---|---|---|
android-overlay-window |
支持 | - | - | webview |
android-system-video-pip |
支持 | - | - | video-url |
ios-in-app-float |
- | 支持 | - | webview |
ios-out-app-pip |
- | 支持 | - | video-url |
harmony-stub |
- | - | stub | - |
说明:
android-system-video-pip与ios-out-app-pip只允许video-url。webview只用于android-overlay-window和ios-in-app-float。- Harmony 当前仅提供 stub,不提供实际窗口宿主。
使用流程
建议在调用 open(...) 前先做支持检查和参数校验:
const support = windowManager.checkSupportSync('android-overlay-window', 'webview')
const validation = windowManager.validateOpenOptionsSync(openOptions)
推荐顺序:
- 用
checkSupportSync(mode, contentType?)判断平台、权限和宿主能力是否满足。 - 用
validateOpenOptionsSync(options)判断当前 payload 是否合法。 - 校验通过后再调用
open(...)。
核心 API
checkSupportSync(mode, contentType?)validateOpenOptionsSync(options)getWindowStateSync(tag)getActiveWindowTagsSync()open(options)refresh(options)close({ tag, ownerPage?, ownerPageId? })updateFrame({ tag, ownerPage?, ownerPageId?, frame })callJS({ tag, ownerPage?, ownerPageId?, script })playVideo({ tag, ownerPage?, ownerPageId? })pauseVideo({ tag, ownerPage?, ownerPageId? })seekVideo({ tag, ownerPage?, ownerPageId?, positionMs })cleanupPageOwnedWindows({ ownerPage?, ownerPageId?, reason? })canDrawOverlaysSync()openOverlayPermissionSettings()bringAppToFront()onWindowEvent(callback)/offWindowEvent(listenerId?)onError(callback)/offError(listenerId?)
open 参数
type FloatingWindowOpenOptions = {
requestId?: string | null
tag: string
mode: FloatingWindowMode
contentType: FloatingWindowContentType
ownerPage?: any | null
ownerPageId?: string | null
frame?: { x?: number | null; y?: number | null; width?: number | null; height?: number | null } | null
appearance?: {
cornerRadius?: number | null
showTitleBar?: boolean | null
showCloseButton?: boolean | null
backgroundColor?: string | null
} | null
behavior?: {
canDrag?: boolean | null
autoRestoreToApp?: boolean | null
replaceExisting?: boolean | null
} | null
webview?: FloatingWindowWebViewContent | null
video?: FloatingWindowVideoContent | null
extra?: UTSJSONObject | null
}
webview
至少提供以下其中一组:
urlhtml+baseUrl?assetPath
示例:
webview: {
assetPath: '/static/pip/demo.html',
jsBridgeEnabled: true,
}
video-url
video: {
url: 'https://example.com/demo.mp4',
title: 'Hans PiP Demo',
subtitle: 'video host',
aspectRatio: '16:9',
autoEnter: true,
isMuted: false,
allowsExternalPlayback: true,
startPositionMs: 5000,
}
示例
Overlay / In-App Float
const openOptions: FloatingWindowOpenOptions = {
tag: 'demo-primary',
mode: 'android-overlay-window',
contentType: 'webview',
ownerPage,
ownerPageId,
frame: { x: 24, y: 160, width: 300, height: 220 },
appearance: {
cornerRadius: 20,
showTitleBar: true,
showCloseButton: true,
backgroundColor: '#FFFDF6',
},
'/static/pip/demo.html',
jsBridgeEnabled: true,
},
}
windowManager.open(openOptions)
System PiP
windowManager.open({
tag: 'demo-system-pip',
mode: 'android-system-video-pip',
contentType: 'video-url',
video: {
url: 'https://example.com/demo.mp4',
title: 'Hans PiP Demo',
autoEnter: true,
},
})
ownerPage 约定
ios-in-app-float 是 page-owned 模式,页面侧需要复用稳定的 ownerPage 和 ownerPageId,并在页面卸载时主动清理:
const pages = getCurrentPages()
const ownerPage = pages.length > 0 ? pages[pages.length - 1] : null
const ownerPageId = `page:${Date.now()}`
onUnload(() => {
windowManager.cleanupPageOwnedWindows({
ownerPage,
ownerPageId,
reason: 'page-unload',
})
})
如果同一窗口的 ownerPage / ownerPageId 不一致,应预期收到 9012011。
JS Bridge
callJS(...) 只对 contentType = 'webview' 可用。
桥对象:
window.HansPipBridge.postMessage(payload)
推荐消息格式:
type FloatingWindowJSBridgeMessage = {
event: string
callbackId?: string | null
data?: UTSJSONObject | null
}
宿主侧通过 onWindowEvent(...) 接收 js-callback 事件。跨端最低保证是 data.rawMessage 会回流到宿主。
常见事件
- 通用:
opened、refreshed、frame-updated、closed - WebView:
webview-ready、webview-error、js-callback - Overlay:
native-close-request、restore-requested、frame-dragged - Android system PiP:
pip-host-ready、pip-entered、pip-refreshed、video-ready、video-rendering-start、video-completed、video-error、restored-to-app - iOS out-app PiP:
video-ready、pip-refreshed、video-played、video-paused、video-seeked、video-error
平台限制
Android
- overlay 模式需要系统授予悬浮窗权限。
canDrawOverlaysSync()仅 Android 可用。openOverlayPermissionSettings()仅 Android 可用。
iOS
ios-out-app-pip依赖宿主具备对应后台音视频能力。ios-in-app-float必须正确传递并复用ownerPage/ownerPageId。bringAppToFront()在 iOS 不保证可用。
错误码
| errCode | 含义 |
|---|---|
9012001 |
当前平台不支持该模式 |
9012002 |
权限或宿主配置缺失 |
9012003 |
参数非法 |
9012004 |
宿主未就绪 |
9012005 |
当前状态不允许此操作 |
9012006 |
原生调用失败 |
9012007 |
系统版本不足 |
9012008 |
能力尚未实现 |
9012009 |
tag 冲突 |
9012010 |
目标窗口不存在 |
9012011 |
页面作用域或宿主上下文无效 |
排障建议
- 先看
checkSupportSync(...).issues。 - 再看
validateOpenOptionsSync(...).issues。 - WebView 场景优先确认是否收到
webview-ready。 - iOS in-app float 场景优先检查
ownerPage / ownerPageId是否稳定。 - Android overlay 场景优先检查权限。

收藏人数:
购买普通授权版(
试用
赞赏(0)
下载 264
赞赏 0
下载 11507006
赞赏 1882
赞赏
京公网安备:11010802035340号