更新记录

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 跳变到正确位置”的视觉错位

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 / 小程序 三端。


📖 目录


介绍

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
路由过渡 ✅ 调用时动态传入 animationTypeanimationDuration
路由过渡 ✅ 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.jsonstyle.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)

  1. 入栈时(navigateTo / redirectTo / reLaunch / switchTab

    • 触发瞬间,对当前 uni-page 做视觉克隆(含滚动位置:uni-page 自身 / 内部 scroll-view / document/body 级原生滚动)
    • 旧页快照以 position: fixed 全屏底板形式(z-index: 1)置于新页之下
    • 新页执行原生入场动画从右滑入叠加在上层
    • 动画结束后旧页快照 不销毁,detach 后以「新页路径」为 key 缓存到池中
  2. 手势返回时

    • 手指刚按下边缘的一瞬间,从池中取出对应 backdrop 重新挂到当前页之下
    • 因为节点 detach 期间 scrollTop 会被浏览器重置为 0,重新挂载时插件会显式 重写 缓存的滚动位置数据
    • 跟手滑动期间,背后是真实的上一页内容
    • 松手后接力动画结束,backdrop 升到顶层覆盖(z-index: 9999)遮蔽 uni 异步 mount 下层页那 1~2 帧;下层页 mount + paint 后才卸下 backdrop,无任何空白闪烁
  3. 生命周期

    • 当用户真正退出某页(栈回退到上一页),对应的 backdrop 会被 disposeBackdrop 自动销毁
    • 同一 key 重复 stash 时,旧 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.navigateBackpopstate,注册一次即可覆盖所有返回入口。

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 拦截 → 调用 onBeforeStart handler → 业务可返回 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

隐私、权限声明

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

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

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

许可协议

MIT协议

暂无用户评论。