更新记录
1.2.0(2026-06-29) 下载此版本
v1.2.0 (2026-06-29)
全端化重大升级:手势返回、离开守卫、二次确认等核心能力从「仅 H5」扩展到「App / 小程序 / H5 三端」。
✨ 新增功能
1.1.0(2026-06-28) 下载此版本
v1.1.0 (2026-06-28)
✨ 新增功能
App 原生级双页堆栈动画
完全对齐 App 端原生路由动画的视觉效果,新页滑入时下方真实显示上一页内容,不再是白底过渡。
-
✅ 旧页快照底板(backdrop)池机制
- 在
navigateTo/redirectTo/reLaunch/switchTab触发时,对旧页uni-page做视觉克隆(含内部滚动位置 /document级原生滚动 / 内部scroll-view等容器) - 旧页快照以底板形式置于新页之下,新页从右滑入叠加显示
- 动画结束后旧页快照不销毁,而是 detach 进 backdrop 池,以新页路径为 key 缓存
- 在
-
✅ 手势返回直接复用底板
- 手指刚按下边缘那一瞬间,从池中取出对应 backdrop 重新挂到当前页之下作为底板显示
- 跟手滑动时背后实时显示真实的上一页内容(含滚动位置),不再是白板
- 体验完全等同 iOS / 安卓原生侧滑返回
-
✅ 滚动位置精准还原
- 节点从 DOM detach 进池子期间,浏览器会把
scrollTop重置为 0 - 内部把
uni-page自身滚动 / 内部scroll-view滚动 /document/body级原生滚动(pageNativeScroll 场景)数据缓存到 ghost 自身属性上 - 重新挂回 DOM 时显式重写
scrollTop/scrollLeft,避免“底板显示未滚动状态 → 真实页 mount 跳变到正确位置”的视觉错位
- 节点从 DOM detach 进池子期间,浏览器会把
1.0.0(2026-06-28) 下载此版本
首个稳定版本发布
查看更多平台兼容性
uni-app(4.61)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| - | √ | √ | √ | √ | - | √ | √ | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | - | √ | - | - | - | - |
jz-page-transition
一款专为 uni-app 打造的 路由过渡动画 + 全端手势返回 + 统一离开守卫 一站式解决方案。 自 v1.2.0 起,手势返回 / 离开守卫 / 二次确认 已全面支持 H5 / App / 小程序 三端。
📖 目录
- 介绍
- 功能特性
- 安装
- 快速开始
- 完整配置项
- 双页堆栈动画(App 原生效果)
- 支持的动画类型
- 页面级配置(4 种方式)
- 手势返回模块
- 离开守卫(脏数据二次确认)
- API 参考
- TransitionView 组件
- 自定义动画类
- 常见场景示例
- 注意事项 & FAQ
- 目录结构
- 更新日志
- 许可证
介绍
jz-page-transition 解决了 uni-app 编译到 H5 端时路由切换无过渡动画 的痛点,并补齐了 App 端原生具备但 H5 端缺失的能力;自 v1.2.0 起进一步把 手势返回 / 离开守卫 / 二次确认 三大核心能力扩展到 App / 小程序:
- 🎬 多种过渡动画(H5)—— slide / fade / zoom / pop / flip / cube …
- 📚 App 原生级双页堆栈动画(H5)—— 新页滑入时下方真实显示上一页内容(含滚动位置),不再是白底过渡
- 👆 iOS 风格边缘手势返回(H5)—— 跟手 + 接力,背景实时显示真实的上一页,体验等同原生
- 📱 App / 小程序边缘手势返回 —— 含 半圆形可视化指示器(类 Android Edge Pull),滑过阈值变色提示"松手即返回"
- 🛡️ 统一离开守卫中心(全端)—— 一次注册,同时覆盖
导航栏返回/uni.navigateBack/浏览器后退/边缘手势 - 跨端
<jz-overlay>浮层组件 —— 一份模板多端通用,内部条件编译处理 teleport / fixed 差异 - ⚡ 零侵入 —— 自动拦截
uni.navigateTo等 API,无需改造业务代码
路由过渡动画与 backdrop 池仅在 H5 平台生效;手势返回 / 离开守卫 / <jz-overlay> 三端可用。
功能特性
| 模块 | 特性 |
|---|---|
| 路由过渡 | ✅ 10 种内置动画类型,支持自定义类扩展 |
| 路由过渡 | ✅ App 原生级双页堆栈动画 —— 旧页快照作底板,新页滑入时下方真实显示上一页(含滚动位置) |
| 路由过渡 | ✅ 自动拦截 5 个 uni 路由 API(navigateTo / navigateBack / redirectTo / reLaunch / switchTab) |
| 路由过渡 | ✅ 三级优先级(调用时参数 > 页面配置 > 全局默认) |
| 路由过渡 | ✅ 自动读取 pages.json 中的 animationType / animationDuration |
| 路由过渡 | ✅ 调用时动态传入 animationType 与 animationDuration |
| 路由过渡 | ✅ GPU 加速、will-change 优化、抑制系统横向手势 |
| 手势返回 | ✅ 左 / 右 / 双侧边缘起手,可配置触发距离与速度 |
| 手势返回 | ✅ 实时跟手动画 + 接力动画,松手即完成 |
| 手势返回 | ✅ 背景实时显示真实上一页(含滚动位置 / scroll-view 滚动 / document 级原生滚动) |
| 手势返回 | ✅ 页面白/黑名单(includes / excludes) |
| 离开守卫 | ✅ 全局守卫 / 页面级守卫 / 脏数据检查器 |
| 离开守卫 | ✅ 一次注册覆盖:导航栏返回 / API 返回 / 浏览器后退 / 边缘手势 |
| 离开守卫 | ✅ 内置确认弹窗,支持自定义 render |
| 工程化 | ✅ 纯 JS + JSDoc,Vue 2 / Vue 3 双支持,可被 TS 项目无障碍引用 |
安装
1. 拷贝插件
将插件目录放入项目的 src/uni_modules 下:
src/uni_modules/jz-page-transition/
来源于 uni_modules 插件市场时,HBuilderX 会自动放置到正确位置。
2. 引入插件
main.ts / main.js:
import { createSSRApp } from 'vue'
import App from './App.vue'
// @ts-ignore
import jzTransition from '@/uni_modules/jz-page-transition'
export function createApp() {
const app = createSSRApp(App)
app.use(jzTransition) // 使用默认配置即可工作
return { app }
}
插件已内置
#ifdef H5条件编译,App / 小程序端app.use调用不会有任何副作用,可放心放在公共入口。
快速开始
最小配置(开箱即用)
app.use(jzTransition)
推荐配置(含手势返回)
app.use(jzTransition, {
enable: true,
defaultType: 'slide-horizontal',
duration: 300,
timingFunction: 'ease-out',
gestureBack: {
enable: true,
direction: 'left', // iOS 风格,从左边缘起手向右滑返回
confirm: { enable: true } // 配合脏数据守卫弹出二次确认
}
})
完整配置项
全局选项
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
enable |
boolean |
true |
总开关,关闭后所有路由走原生 |
defaultType |
TransitionType |
'slide-horizontal' |
默认动画类型,见动画列表 |
duration |
number |
300 |
动画时长(毫秒),建议 200~400 |
timingFunction |
string |
'ease-out' |
CSS 缓动函数 |
usePagesJson |
boolean |
true |
是否读取 pages.json 的 style.app-plus.animationType |
pageConfig |
object |
{} |
页面级配置,优先级高于 pages.json |
gestureBack |
object |
undefined |
手势返回子模块配置(不传则不启用) |
pageConfig 写法
pageConfig: {
// 简写:仅指定动画类型
'/pages/index/index': 'fade',
// 详细:可单独配置时长、缓动、是否启用
'/pages/detail/detail': {
type: 'zoom-fade',
duration: 400,
timingFunction: 'ease-in',
enable: true
}
}
gestureBack 选项
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
enable |
boolean |
false |
是否启用手势返回 |
direction |
'left' \| 'right' \| 'both' \| 'none' |
'left' |
起手所在的屏幕边缘 |
edgeWidth |
number |
30 |
边缘触发宽度(px) |
threshold |
number |
0.3 |
触发返回的距离阈值(占屏宽比例) |
velocity |
number |
0.3 |
触发返回的速度阈值(px/ms) |
followFinger |
boolean |
true |
是否启用跟手动画 |
includes |
string[] |
[] |
白名单:仅这些路径启用 |
excludes |
string[] |
[] |
黑名单:这些路径禁用 |
confirm |
object |
见下表 | 二次确认弹窗配置 |
onBack |
(ctx) => void |
null |
成功返回时回调 |
onCancel |
(ctx) => void |
null |
用户取消时回调 |
gestureBack.confirm 选项
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
enable |
boolean |
false |
是否启用二次确认弹窗 |
title |
string |
'提示' |
弹窗标题 |
content |
string |
'当前内容未保存,确认离开吗?' |
弹窗内容 |
confirmText |
string |
'离开' |
确认按钮文案 |
cancelText |
string |
'继续编辑' |
取消按钮文案 |
confirmColor |
string |
'#e64340' |
确认按钮颜色 |
render |
Function \| null |
null |
自定义渲染:(opts) => Promise<boolean> |
双页堆栈动画(App 原生效果)
v1.1.0 起默认启用,无需额外配置。
是什么
参考 App / iOS 原生路由动画的视觉效果 —— 新页滑入时,下方真实显示着上一页内容(含滚动位置),而不是白底过渡。同样的能力也用于手势返回时背景层。
怎么做到的
插件内部维护了一个 旧页快照池(backdrop pool):
-
入栈时(
navigateTo/redirectTo/reLaunch/switchTab)- 触发瞬间,对当前
uni-page做视觉克隆(含滚动位置:uni-page自身 / 内部scroll-view/document/body级原生滚动) - 旧页快照以
position: fixed全屏底板形式(z-index: 1)置于新页之下 - 新页执行原生入场动画从右滑入叠加在上层
- 动画结束后旧页快照 不销毁,detach 后以「新页路径」为 key 缓存到池中
- 触发瞬间,对当前
-
手势返回时
- 手指刚按下边缘的一瞬间,从池中取出对应 backdrop 重新挂到当前页之下
- 因为节点 detach 期间
scrollTop会被浏览器重置为 0,重新挂载时插件会显式 重写 缓存的滚动位置数据 - 跟手滑动期间,背后是真实的上一页内容
- 松手后接力动画结束,backdrop 升到顶层覆盖(z-index: 9999)遮蔽 uni 异步 mount 下层页那 1~2 帧;下层页 mount + paint 后才卸下 backdrop,无任何空白闪烁
-
生命周期
- 当用户真正退出某页(栈回退到上一页),对应的 backdrop 会被
disposeBackdrop自动销毁 - 同一 key 重复 stash 时,旧 backdrop 会先被销毁,避免内存泄露
- 当用户真正退出某页(栈回退到上一页),对应的 backdrop 会被
API(高级用法)
正常业务无需调用这些方法,仅在需要自定义控制时使用:
// 取出指定 key 的 backdrop 节点(不从池中移除)
this.$transition.getBackdrop('/pages/list/list')
// 把指定 key 的 backdrop 挂到当前栈顶下作为底板
this.$transition.showBackdrop('/pages/list/list')
// 卸下当前挂载的 backdrop(仍保留在池中以备复用)
this.$transition.hideBackdrop()
// 销毁指定 key 的 backdrop(彻底清理)
this.$transition.disposeBackdrop('/pages/list/list')
注意事项
- 仅作用于 H5 平台;App / 小程序本就原生具备同等能力
- 克隆只取视觉快照,不会触发组件
mounted/ 网络请求,性能开销极低 - 含
position: fixed子元素(如导航栏、底部 bar)的页面定位 1:1 等价于原页 <video>/<canvas>等节点的克隆是静态视觉副本(不含播放状态),如有特殊需求可手动disposeBackdrop(key)关闭某页的底板缓存
支持的动画类型
| 类型 | 效果说明 | 适用场景 |
|---|---|---|
slide-horizontal |
水平滑动(默认,iOS 风格) | 列表 → 详情 |
slide-vertical |
垂直滑动 | 从底部弹出页面 |
fade |
淡入淡出 | 同级页切换、Tab |
zoom |
缩放 | 弹窗、图片预览 |
zoom-fade |
缩放 + 淡入淡出 | 卡片切换 |
pop |
弹出(带弹性) | 模态框 |
flip-horizontal |
水平 3D 翻转 | 特殊创意 |
flip-vertical |
垂直 3D 翻转 | 特殊创意 |
cube |
立方体旋转 | 营销页 |
none |
无动画 | 禁用 |
App 端 animationType 自动映射
为兼容业务代码中已有的 App 端写法,插件会自动把 animationType 映射到内部类型:
App 端 animationType |
内部类型 |
|---|---|
pop-in / slide-in-right |
slide-horizontal |
slide-in-bottom |
slide-vertical |
slide-in-left |
slide-horizontal-reverse |
fade-in / fade-out |
fade |
zoom-in / zoom-out |
zoom |
zoom-fade-in / zoom-fade-out |
zoom-fade |
pop-out |
pop |
flip-in / cube-in |
flip-horizontal / cube |
none |
none |
页面级配置(4 种方式)
优先级(高 → 低): 调用时参数 >
pageConfig>pages.json> 全局默认
方式一:pageConfig 全局指定
app.use(jzTransition, {
pageConfig: {
'/pages/detail/detail': { type: 'fade', duration: 200 },
'/pages/login/login': 'pop'
}
})
方式二:pages.json 配置(无侵入,推荐)
{
"path": "pages/detail/detail",
"style": {
"app-plus": {
"animationType": "fade-in",
"animationDuration": 250
}
}
}
方式三:调用时动态传参(最灵活)
uni.navigateTo({
url: '/pages/detail/detail',
animationType: 'fade-in', // 兼容 App 写法
animationDuration: 250
})
uni.navigateBack({
delta: 1,
animationType: 'fade',
animationDuration: 200
})
uni.redirectTo({
url: '/pages/login/login',
animationType: 'pop',
animationDuration: 250
})
自定义参数(
animationType/animationDuration)会被插件自动剥离,不会触发 uni 的参数校验错误。
方式四:页面内使用 <TransitionView> 组件
<template>
<transition-view type="fade" :duration="200">
<!-- 页面内容 -->
</transition-view>
</template>
手势返回模块
v1.1.0 起,手势滑动期间背景层会真实显示上一页内容(含滚动位置),完全对齐 iOS / 安卓原生侧滑返回的视觉。详见双页堆栈动画。
启用
app.use(jzTransition, {
gestureBack: {
enable: true,
direction: 'left', // iOS 风格
edgeWidth: 30,
threshold: 0.3,
velocity: 0.3
}
})
仅在部分页面启用
gestureBack: {
enable: true,
includes: ['/pages/detail/detail', '/pages/order/order']
}
排除某些页面
gestureBack: {
enable: true,
excludes: ['/pages/login/login']
}
在页面中订阅手势返回事件
任意 .js / .vue 中(无需 setup 上下文):
import { gestureBack } from '@/uni_modules/jz-page-transition'
const off = gestureBack.addGuard((ctx) => {
// ctx: { from, direction, key }
console.log('手势触发返回', ctx)
return true // true=放行;false=拦截;object=弹窗配置
})
// 页面卸载时取消订阅
onUnmounted(off)
Vue 3 <script setup> 推荐写法:
import { useGestureBack } from '@/uni_modules/jz-page-transition'
const gb = useGestureBack()
const off = gb.on('order-edit', (ctx) => {
if (!dirty.value) return true
return { content: '订单尚未保存,确认离开吗?' }
})
onUnmounted(off)
离开守卫(脏数据二次确认)
registerDirty() 是本插件最有特色的能力 —— 一次注册,覆盖所有返回入口:
- ✅ 顶部导航栏返回按钮
- ✅ 业务代码调用
uni.navigateBack() - ✅ 浏览器后退 / 物理返回键 /
popstate - ✅ 边缘手势返回
基础用法
import { gestureBack } from '@/uni_modules/jz-page-transition'
import { ref, onUnmounted } from 'vue'
const dirty = ref(false)
const off = gestureBack.registerDirty(
'order-edit', // 页面 key(可选)
() => dirty.value, // 脏数据检查器,返回 true 时拦截
{ // 弹窗文案覆盖(可选)
title: '提示',
content: '订单尚未保存,确认离开吗?',
confirmText: '离开',
cancelText: '继续编辑'
}
)
onUnmounted(off)
简化签名(不需要 key)
gestureBack.registerDirty(() => dirty.value)
gestureBack.registerDirty(() => dirty.value, { content: '确认离开?' })
自定义确认 UI
app.use(jzTransition, {
gestureBack: {
enable: true,
confirm: {
enable: true,
render(options) {
// 返回 Promise<boolean>,true 表示放行
return new Promise((resolve) => {
myCustomModal.show({
...options,
onConfirm: () => resolve(true),
onCancel: () => resolve(false)
})
})
}
}
}
})
API 参考
app.config.globalProperties.$transition
// 设置全局配置
this.$transition.setDefaultConfig({ enable: false })
// 获取当前配置
this.$transition.getConfig()
// 获取动画实例
this.$transition.getTransition('fade', { duration: 250 })
// 获取页面配置
this.$transition.getPageConfig('/pages/detail/detail')
// 注册自定义动画
this.$transition.registerTransition('my-anim', MyTransitionClass)
// 取消自定义动画
this.$transition.unregisterTransition('my-anim')
// 查询支持的所有动画类型
this.$transition.getSupportedTypes()
// 获取页面栈快照
this.$transition.getPageStack()
// === Backdrop 池(双页堆栈动画用,v1.1.0 新增) ===
// 取出指定 key 的 backdrop 节点(不从池中移除)
this.$transition.getBackdrop('/pages/list/list')
// 把指定 key 的 backdrop 挂到当前栈顶下作为底板
this.$transition.showBackdrop('/pages/list/list')
// 卸下当前挂载的 backdrop(仍保留在池中以备复用)
this.$transition.hideBackdrop()
// 销毁指定 key 的 backdrop(彻底清理)
this.$transition.disposeBackdrop('/pages/list/list')
gestureBack 代理对象(推荐)
任何文件、任何时机调用都安全,内部用 Proxy 取最新单例:
import { gestureBack } from '@/uni_modules/jz-page-transition'
gestureBack.addGuard(guard) // 全局守卫
gestureBack.on(key, handler) // 页面级守卫
gestureBack.registerDirty(key?, checker, confirm?) // 脏数据检查
gestureBack.setConfig(partial) // 动态修改配置
gestureBack.getConfig() // 获取当前配置
gestureBack.enable() // 启用
gestureBack.disable() // 禁用
Composition API
import { useGestureBack, getGestureBack } from '@/uni_modules/jz-page-transition'
// 在 setup 中
const gb = useGestureBack() // 优先 inject,兜底单例
// 在普通 js 中
const gb = getGestureBack() // 取全局单例
TransitionView 组件
需要更细粒度地控制某个区域的过渡时使用:
<template>
<transition-view
:enable="true"
type="fade"
:duration="300"
timing-function="ease-out"
@before-enter="onBeforeEnter"
@after-enter="onAfterEnter"
>
<!-- 内容 -->
</transition-view>
</template>
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
enable |
Boolean |
true |
是否启用 |
type |
String |
'' |
动画类型,留空时跟随全局 |
duration |
Number |
300 |
动画时长 |
timingFunction |
String |
'ease-out' |
缓动函数 |
自定义动画类
继承 BaseTransition 即可:
import { BaseTransition } from '@/uni_modules/jz-page-transition'
class MyTransition extends BaseTransition {
beforeEnter(el) {
el.style.opacity = '0'
el.style.transform = 'translateY(100%)'
}
enter(el, done) {
el.style.transition = `all ${this.duration}ms ${this.timingFunction}`
el.style.opacity = '1'
el.style.transform = 'translateY(0)'
setTimeout(done, this.duration)
}
afterEnter(el) {
el.style.transition = ''
}
beforeLeave(el) { /* ... */ }
leave(el, done) { /* ... */ }
afterLeave(el) { /* ... */ }
}
// 注册
this.$transition.registerTransition('my-anim', MyTransition)
// 使用
uni.navigateTo({ url: '/pages/x/x', animationType: 'my-anim' })
常见场景示例
1. 详情页用 fade,其他页用默认
app.use(jzTransition, {
pageConfig: { '/pages/detail/detail': 'fade' }
})
2. 关闭某个页面的动画
app.use(jzTransition, {
pageConfig: {
'/pages/loading/loading': { enable: false }
}
})
3. 表单页防误返回
// pages/order-edit.vue
import { gestureBack } from '@/uni_modules/jz-page-transition'
const dirty = ref(false)
onMounted(() => {
const off = gestureBack.registerDirty(() => dirty.value)
onUnmounted(off)
})
4. 调用时临时变更动画
uni.navigateTo({
url: '/pages/preview/preview',
animationType: 'zoom-fade',
animationDuration: 400
})
5. 运行时切换全局动画
// 弱网下关闭动画提速
if (slowNetwork) {
this.$transition.setDefaultConfig({ enable: false })
}
注意事项 & FAQ
Q1:插件在 App / 小程序端会生效吗?
不会。 插件入口已用 #ifdef H5 条件编译包裹,App / 小程序端的 app.use 调用会被视为 no-op,不影响原生动画体验。
Q2:手势返回和 iOS Safari 自带的边缘返回冲突?
插件已在全局注入 overscroll-behavior-x: contain + touch-action: pan-y,抑制了系统横向手势,整屏由 TouchTracker 接管。
Q3:tabBar 页面有动画吗?
switchTab 默认使用 fade,业务代码可通过传入 animationType 自定义;如果完全不需要,使用 'none'。
Q4:浏览器后退按钮会触发 registerDirty 吗?
会。 leave-guard 中心同时接管了 uni.navigateBack 和 popstate,注册一次即可覆盖所有返回入口。
Q5:动画期间能否再次触发返回?
插件内部用 isAnimating 标记防止重复触发,动画期间的 navigateBack 会直接走原生方法以避免死锁。
v1.2.0 新增:App / 小程序端手势返回与跨端浮层
<gesture-view> 包裹组件
一份代码三端通用。H5 下退化为占位容器(手势仍由全局 TouchTracker 处理);App / 小程序下接管 touch 事件并提供半圆形可视化指示器。
<template>
<gesture-view
:enable="true"
direction="left"
:edge-width="30"
:trigger-distance="15"
:threshold="0.3"
page-key="my-page"
>
<!-- 页面内容 -->
</gesture-view>
</template>
<script setup>
import GestureView from '@/uni_modules/jz-page-transition/components/gesture-view/gesture-view.vue'
</script>
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
enable |
Boolean | true |
是否启用手势 |
direction |
'left'\|'right'\|'both' |
'left' |
起手边缘方向 |
edgeWidth |
Number | 30 |
边缘判定范围(px) |
triggerDistance |
Number | 15 |
触发判定的最小滑动距离 |
threshold |
Number | 0.3 |
触发返回的距离阈值(屏宽占比) |
velocity |
Number | 0.3 |
触发速度阈值 |
pageKey |
String | '' |
页面 key(用于钩子匹配) |
App / 小程序:半圆形可视化指示器
App / 小程序原生没有跟手动画,组件会在边缘起手时露出半圆形 indicator:
- 贴着手指 Y 坐标垂直移动
- 露出宽度(0 ~ 80px)随滑动距离增长,透明度随进度上升
- 中心三角箭头指向滑动方向
- 滑过阈值瞬间,背景由黑变蓝 + 箭头放大 15%,告诉用户"松手即返回"
- 抬手 / 取消 → 200ms 淡出
滑动结束的行为
- H5:保留跟手 + 接力 + backdrop 的原生级体验
- App / 小程序:达到阈值即调用
uni.navigateBack(),与点击导航栏返回完全相同的路径 → 被leave-guard拦截 → 调用onBeforeStarthandler → 业务可返回ConfirmConfig/Promise<boolean>自定义弹窗
<jz-overlay> 跨端浮层组件
业务页只写一份模板,组件内部自动处理平台差异(H5 teleport / App-小程序内联)。
<template>
<jz-overlay :visible="showDialog" @mask-click="onCancel">
<view class="my-dialog">
<text>当前内容未保存,确认离开吗?</text>
<view @click="onConfirm">确认离开</view>
<view @click="onCancel">继续编辑</view>
</view>
</jz-overlay>
</template>
<script setup>
import { ref } from 'vue'
import JzOverlay from '@/uni_modules/jz-page-transition/components/jz-overlay/jz-overlay.vue'
const showDialog = ref(false)
function onConfirm() { showDialog.value = false }
function onCancel() { showDialog.value = false }
</script>
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
visible |
Boolean | false |
是否显示 |
maskClosable |
Boolean | true |
点遮罩是否触发 mask-click 并 emit update:visible |
zIndex |
Number | 9999 |
层级 |
background |
String | rgba(0,0,0,0.45) |
遮罩颜色 |
| Event | 触发时机 |
|---|---|
mask-click |
点击遮罩 |
update:visible |
与 v-model:visible 双向绑定 |
全端自定义二次确认弹窗范式
<template>
<gesture-view page-key="form-edit">
<!-- 表单 -->
</gesture-view>
<!-- 一份模板多端通用 -->
<jz-overlay :visible="show" @mask-click="onCancel">
<view class="my-dialog">...</view>
</jz-overlay>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { gestureBack } from '@/uni_modules/jz-page-transition'
import GestureView from '@/uni_modules/jz-page-transition/components/gesture-view/gesture-view.vue'
import JzOverlay from '@/uni_modules/jz-page-transition/components/jz-overlay/jz-overlay.vue'
const dirty = ref(true)
const show = ref(false)
let resolver = null
function onConfirm() { show.value = false; resolver && resolver(true); resolver = null }
function onCancel() { show.value = false; resolver && resolver(false); resolver = null }
let off = null
onMounted(() => {
// onBeforeStart 同时覆盖:边缘手势 / 导航栏返回 / uni.navigateBack / 浏览器后退
off = gestureBack.onBeforeStart('form-edit', async (ctx) => {
if (!dirty.value) return true // 无脏数据 → 放行
return new Promise((resolve) => { // 自定义弹窗
resolver = resolve
show.value = true
})
})
})
onUnmounted(() => { off && off(); resolver && resolver(false) })
</script>
AppContent 透传 gesture-view 配置
如果你像 demo 一样有一个 AppContent 通用页骨架,可以让它对外暴露 6 个透传 prop:
<app-content
navBarTitle="编辑页"
gesture-page-key="form-edit"
:gesture-enable="true"
:gesture-edge-width="40"
>
<!-- 业务内容 -->
</app-content>
| Prop | 默认 |
|---|---|
gestureEnable |
true |
gestureDirection |
'left' |
gestureEdgeWidth |
30 |
gestureTriggerDistance |
15 |
gestureThreshold |
0.3 |
gesturePageKey |
'' |
onBeforeStart 钩子返回值
| 返回值 | 含义 |
|---|---|
true / undefined |
放行 |
false |
拦截,不弹窗 |
ConfirmConfig 对象 { title, content, confirmText, cancelText } |
使用 SDK 内置 uni.showModal 弹窗 |
Promise<boolean> |
由业务自定义弹窗 / 自定义 UI,resolve true 放行 / false 拦截 |
Promise<ConfirmConfig> |
同上但可异步生成弹窗文案 |
钩子在 三个返回入口 均会触发:
- 边缘手势(H5 跟手到位 / App 滑过阈值松手)
- 点击导航栏返回按钮 →
uni.navigateBack - 浏览器后退 / 物理键 /
popstate
Q6:如何完全卸载/暂停插件?
this.$transition.setDefaultConfig({ enable: false }) // 暂停过渡
gestureBack.disable() // 暂停手势
this.$transition.restoreRouter() // 还原 uni 路由 API
Q7:性能优化建议
- 动画时长建议 200~300ms
- 长列表页慎用
cube/flip等 3D 动画 - 在
<TransitionView>内部尽量避免高频 reactive 重渲染
Q8:双页堆栈动画的 backdrop 缓存会不会爆?
不会。每个旧页只缓存一份 DOM 快照,按页面路径作 key;同一 key 重复 stash 时旧节点会被销毁;用户真正退出某页时对应 backdrop 也会被 disposeBackdrop 清理。普通业务下池中最多保留 N-1 个节点(N = 当前页面栈深度)。
Q9:某些页面(如带 <video> 直播)不想被克隆作背景?
克隆是静态视觉副本,对 <video> / <canvas> 等运行时节点只取当前帧画面。如有特殊需求,可在进入该页前手动调用:
this.$transition.disposeBackdrop('/pages/live/live')
即可关闭该页的底板缓存。
目录结构
src/uni_modules/jz-page-transition/
├── package.json
├── readme.md
├── changelog.md
├── index.js # 插件入口(install 函数)
├── components/
│ └── transition-view/
│ └── transition-view.vue # 过渡动画容器组件
├── core/
│ ├── base-transition.js # 动画基类
│ ├── transition-manager.js # 动画管理器 / 路由拦截
│ └── pages-json-reader.js # pages.json 读取器
├── transitions/
│ ├── slide-transition.js
│ ├── fade-transition.js
│ ├── zoom-transition.js
│ ├── zoom-fade-transition.js
│ ├── pop-transition.js
│ ├── flip-transition.js
│ ├── cube-transition.js
│ └── none-transition.js
├── utils/
│ ├── dom.js # DOM 操作工具
│ ├── animation.js # 动画工具函数
│ └── platform.js # 平台检测
├── config/
│ ├── default.js # 默认配置
│ └── constants.js # 常量定义
└── js_sdk/
├── gesture-back/ # 手势返回模块
│ ├── index.js
│ ├── manager.js # GestureBackManager
│ ├── touch-tracker.js # 触摸跟踪器
│ ├── back-resolver.js # 守卫链调度
│ ├── confirm-dialog.js # 确认弹窗
│ └── constants.js
└── router-guard/
└── leave-guard.js # 离开守卫中心(统一入口)
更新日志
详见 changelog.md。
许可证
MIT © jz-team

收藏人数:
下载插件并导入HBuilderX
下载插件ZIP
赞赏(0)
下载 2604
赞赏 0
下载 12500585
赞赏 1940
赞赏
京公网安备:11010802035340号