更新记录

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 H5 端打造的 路由过渡动画 + 边缘手势返回 + 离开守卫 一站式解决方案。


📖 目录


介绍

jz-page-transition 解决了 uni-app 编译到 H5 端时路由切换无过渡动画 的痛点,并补齐了 App 端原生具备但 H5 端缺失的能力:

  • 🎬 多种过渡动画 —— slide / fade / zoom / pop / flip / cube …
  • 👆 iOS 风格边缘手势返回 —— 跟手 + 接力,体验等同原生
  • 🛡️ 统一离开守卫中心 —— 一次注册,同时覆盖 导航栏返回 / uni.navigateBack / 浏览器后退 / 边缘手势
  • 零侵入 —— 自动拦截 uni.navigateTo 等 API,无需改造业务代码

仅在 H5 平台生效,其他平台自动绕过,不影响 App / 小程序的原生行为


功能特性

模块 特性
路由过渡 ✅ 10 种内置动画类型,支持自定义类扩展
路由过渡 ✅ 自动拦截 5 个 uni 路由 API(navigateTo / navigateBack / redirectTo / reLaunch / switchTab)
路由过渡 ✅ 三级优先级(调用时参数 > 页面配置 > 全局默认)
路由过渡 ✅ 自动读取 pages.json 中的 animationType / animationDuration
路由过渡 ✅ 调用时动态传入 animationTypeanimationDuration
路由过渡 ✅ GPU 加速、will-change 优化、抑制系统横向手势
手势返回 ✅ 左 / 右 / 双侧边缘起手,可配置触发距离与速度
手势返回 ✅ 实时跟手动画 + 接力动画,松手即完成
手势返回 ✅ 页面白/黑名单(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>

支持的动画类型

类型 效果说明 适用场景
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>

手势返回模块

启用

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()

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 会直接走原生方法以避免死锁。

Q6:如何完全卸载/暂停插件?

this.$transition.setDefaultConfig({ enable: false })   // 暂停过渡
gestureBack.disable()                                  // 暂停手势
this.$transition.restoreRouter()                       // 还原 uni 路由 API

Q7:性能优化建议

  • 动画时长建议 200~300ms
  • 长列表页慎用 cube / flip 等 3D 动画
  • <TransitionView> 内部尽量避免高频 reactive 重渲染

目录结构

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协议

暂无用户评论。