自定义路由 您所在的位置:网站首页 mapbuilder怎么用 自定义路由

自定义路由

2023-03-13 15:22| 来源: 网络整理| 查看: 265

自定义路由 # 自定义路由

小程序采用多 WebView 架构,页面间跳转形式十分单一,仅能从右到左进行动画。而原生 App 的动画形式则多种多样,如从底部弹起,下一个为半屏页面,前一个页面下沉等。

Skyline 渲染引擎下,页面有两种渲染模式: WebView 和 Skyline,它们通过页面配置中的 renderer 字段进行区分。在连续的 Skyline 页面间跳转时,可实现自定义路由效果,路由动画的曲线、时长均可交由开发者控制。

# 立即体验

扫码打开小程序示例,交互动画 - 基础组件 - 自定义路由 即可体验。

# 使用步骤

使用自定义路由分为 3 个步骤:

定义路由动画 页面跳转时指定路由类型 绑定页面手势 # 步骤一:定义路由动画

定义路由动画的主要步骤是注册一个 builder 函数,来定义小程序页面间切换过程的动画过程。

builder 函数接受的参数均为 sharedValue 类型,当路由事件发生时,Skyline 引擎会同步修改它们的值。开发者在 builder 函数中需要实现的是 handlePrimaryAnimation 和 handleSecondaryAnimation 这两个动画函数,当使用的 sharedValue 变化时,对应的 handlePrimaryAnimation / handleSecondaryAnimation 函数就会被执行。

// 注册 builder 函数wx.router.addRouteBuilder("Cupertino", CupertinoRouteBuilder)// 实现 builder 函数const CupertinoRouteBuilder = ({ primaryAnimation, secondaryAnimation, primaryAnimationStatus, secondaryAnimationStatus, userGestureInProgress}) => { // 实现 worklet 动画函数 handlePrimaryAnimation 、 handleSecondaryAnimation const { windowWidth } = wx.getWindowInfo(); const handlePrimaryAnimation = () => { "worklet"; // primaryAnimation 是 builder 函数的入参 const translateX = windowWidth * (1 - primaryAnimation.value) return { transform: `translateX(${translateX}px)` } } const handleSecondaryAnimation = ... return { handlePrimaryAnimation, handleSecondaryAnimation, opaque: true, transitionDuration: 300, reverseTransitionDuration: 300, barrierColor: "", barrierDismissible: false, barrierLabel: "", canTransitionTo: true, canTransitionFrom: true, }}# builder 函数 # 入参 属性 类型 说明 primaryAnimation sharedValue 页面进入和退出时的动画进度,取值:0 - 1 secondaryAnimation sharedValue 下一个页面进入时,当前页面的动画进度,0-1 primaryAnimationStatus AnimationStatus: sharedValue primaryAnimation 的状态 AnimationStatus 值 说明 0 动画一开始就停止了 1 动画从头到尾进行中 2 动画从尾到头进行中 3 结束动画? secondaryAnimationStatus AnimationStatus: sharedValue secondaryAnimation 的状态 userGestureInProgress sharedValue 手势进行中状态 # 出参 参数 类型 默认值 说明 handlePrimaryAnimation worklet 动画函数 返回 AnimatedStyle,控制页面的进入和退出动画 handleSecondaryAnimation worklet 动画函数 返回 AnimatedStyle,控制下一个页面进入时,当前页面的动画 opaque boolean true 页面推入后,原栈顶页面是否可见,true:不可见,false:可见 transitionDuration number 300 页面推入动画时长 reverseTransitionDuration number 300 页面退出动画时长 barrierColor string 页面推入时的遮罩层背景色,默认无 barrierDismissible boolean false 点击遮罩是否返回上一页 barrierLabel string 无障碍语义 canTransitionTo boolean true 下一个页面推入时,当前页 secondaryAnimation 是否生效 canTransitionFrom boolean true 当前页推入时,前一个页面 secondaryAnimation 是否生效# worklet 动画函数

builder 函数中的 handlePrimaryAnimation 和 handleSecondaryAnimation 都是 worklet 动画函数,返回 AnimatedStyle,在路由过程中在 UI 线程执行,不会阻塞 JS 线程。

primaryAnimation 默认是线性的,为了丰富页面跳转的动画效果,worklet 支持了常见的动画缓动函数 Easing,开发者可以根据业务需求实现需要的页面切换动画效果。

/* * CurveAnimation 利用 derivedValue 机制 * 封装了进入和退出时的动画曲线 */ function CurveAnimation ({ animation, animationStatus, curve, reverseCurve }) { const { derived } = wx.worklet; return derived(() => { 'worklet'; const useForwardCurve = !reverseCurve || animationStatus.value !== AnimationStatus.reverse; const activeCurve = useForwardCurve %2f curve : reverseCurve; const t = animation.value; if (!activeCurve) return t; if (t === 0 || t === 1) return t; return activeCurve(t); }); } /** * worklet 支持了常见的动画缓动函数 * 示例使用 bezier 三次贝塞尔曲线 */ const Curves = {}; if (wx.worklet) { const { Easing } = wx.worklet; Object.assign(Curves, { fastLinearToSlowEaseIn: Easing.bezier(0.18, 1.0, 0.04, 1.0).factory(), linearToEaseOut: Easing.bezier(0.35, 0.91, 0.33, 0.97).factory(), easeInToLinear: Easing.bezier(0.67, 0.03, 0.65, 0.09).factory(), fastOutSlowIn: Easing.bezier(0.4, 0.0, 0.2, 1.0).factory(), }); } const { windowWidth } = wx.getWindowInfo(); const _curvePrimaryAnimation = CurveAnimation({ animation: primaryAnimation, // builder 函数入参 animationStatus: primaryAnimationStatus, // builder 函数入参 curve: Curves.linearToEaseOut, reverseCurve: Curves.easeInToLinear, }); /** * primaryAnimation 控制页面进入和退出动画 * 1. 页面进入时采用 curve 曲线生成的值 * 2. 页面退出时采用 reverseCurve 生成的值 */ const handlePrimaryAnimation = () => { "worklet"; let t = primaryAnimation.value; if (!userGestureInProgress.value) { t = _curvePrimaryAnimation.value; } const translateX = windowWidth * (1 - t) return { transform: `translateX(${translateX}px)` }; };# 步骤二:页面跳转时指定路由类型 // 低版本基础库会降级到多 webview 下的路由动画wx.navigateTo({ url: 'pageB', routeType: 'Cupertino',})# 步骤三:绑定页面手势

通常我们会在页面上绑定手势,除了用户点击按钮返回外,还可以向右、向下拖动页面返回。此时就需要在页面内绑定手势进行处理。

绑定手势之后,根据 GestureState 状态值来接管页面动画,手势事件 this.customRouteContext 包含当前页面定义路由 builder 时的全部变量。

在手势事件中根据手势来修改 primaryAnimation 的值实现页面手势动画。

# GestureState 状态值 值 说明 0 POSSIBLE 此时手势未识别,如 panDown等 1 BEGIN 手势已识别 2 ACTIVE 连续手势活跃状态 3 END 手势终止 4 CANCELLED 手势取消# this.customRouteContext 属性

手势接管开始必需调用 startUserGesture,手势结束必须调用 stopUserGesture,退出页面必须调用 didPop。

Skyline 引擎无法内部处理掉这些调用,原因是引擎无法得知开发者最终是否想退出这个页面。

属性 类型 说明 didPop function 退出页面时调用事件 startUserGesture function 开始页面手势事件,更新 userGesturelnProgress 值为 1 stopUserGesture function 结束页面手势事件,更新 userGesturelnProgress 值为 0 skylinePageld sharedValue secondaryAnimation sharedValue userGesturelnProgress sharedValue primaryAnimationStatus sharedValue primaryAnimation sharedValue skylineWindowld sharedValue secondaryAnimationStatus sharedValue routeType string # 示例代码 /** * 1. customRouteContext 中包含当前页面定义路由 builder 时的全部变量 * 2. 可以从中获取 primaryAnimation,对其进行修改,进而驱动页面动画 * 3. 手势返回时由开发者控制 primaryAnimation 的更新 * 4. 接口调用页面进入和退出时,由 skyline 引擎控制 primaryAnimation 的更新 */Page({ handleDragStart() { "worklet"; const { startUserGesture } = this.customRouteContext; // 触摸开始时需要调用 startUserGesture // 更新 userGestureInProgress 的值 // ⚠️⚠️ startUserGesture(); }, handleDragUpdate(delta) { "worklet"; const { primaryAnimation } = this.customRouteContext; const newVal = primaryAnimation.value - delta; // 跟随手指拖动页面效果 primaryAnimation.value = clamp(newVal, 0.0, 1.0); }, handleDragEnd(velocity) { "worklet"; const { primaryAnimation, stopUserGesture, didPop } = this.customRouteContext; // 页面滑动超过一半或者手指离开时的释放速度大于指定值时 // 判定最终要退出页面,继续滑动页面到右侧边缘 // 否则判定取消此时退出,页面返回到左侧边缘 let animateForward = false; if (Math.abs(velocity) >= _kMinFlingVelocity) { animateForward = velocity 0.5; } const t = primaryAnimation.value; const animationCurve = Curves.fastLinearToSlowEaseIn; if (animateForward) { const droppedPageForwardAnimationTime = Math.min( Math.floor(lerp(_kMaxDroppedSwipePageForwardAnimationTime, 0, t)), _kMaxPageBackAnimationTime ); // primaryAnimation.vaue = 1 表示未退出页面 primaryAnimation.value = timing( 1.0, { duration: droppedPageForwardAnimationTime, easing: animationCurve, }, () => { // 最终均需调用 stopUserGesture // ⚠️⚠️ stopUserGesture(); } ); } else { const droppedPageBackAnimationTime = Math.floor( lerp(0, _kMaxDroppedSwipePageForwardAnimationTime, t) ); // primaryAnimation.vaue = 0 表示退出页面 primaryAnimation.value = timing( 0.0, { duration: droppedPageBackAnimationTime, easing: animationCurve, }, () => { // 退出页面时需调用 didPop // ⚠️⚠️ didPop(); // 最终均需调用 stopUserGesture // ⚠️⚠️ stopUserGesture(); } ); } }, handleHorizontalDrag(gestureEvent) { "worklet"; if (gestureEvent.state === GestureState.BEGIN) { this.handleDragStart(); } else if (gestureEvent.state === GestureState.ACTIVE) { const delta = gestureEvent.deltaX / windowWidth; this.handleDragUpdate(delta); } else if (gestureEvent.state === GestureState.END) { const velocity = gestureEvent.velocityX / windowWidth; this.handleDragEnd(velocity); } else if (gestureEvent.state === GestureState.CANCELLED) { this.handleDragEnd(0.0); } }})# 原理说明

route

当前页面栈顶为 PageA,当调用 wx.navigateTo 推入 PageB 时:

PageA 对应的 secondaryAnimation 从 0.0 -> 1.0,PageB 的 primaryAnimation 从 0.0 -> 1.0

此时页面栈顶变为 PageB,当调用 wx.navigateBack 推出 PageB时:

PageA 对应的 secondaryAnimation 从 1.0 -> 0.0,PageB 的 primaryAnimation 从 1.0 -> 0.0

primaryAnimation 和 secondaryAnimation 均为 SharedValue 类型(如果你对如何使用 SharedValue 还不熟悉,可以先看看 worklet 文档),表示路由的进度。当路由事件发生时,Skyline 引擎会同步修改它们的值。

handlePrimaryAnimation 和 handleSecondaryAnimation 返回 AnimatedStyle,它们的作用跟 this.applyAnimaedStyle 接口是一样,只不过作用于页面的 “根” 节点。

userGestureInProgress 表示当前是否处于手势返回过程中。默认路由效果下,手势拖动页面返回时,页面动画会是一个线性的,跟随手移动的状态。而调用接口返回时,则是不同的曲线。

# 开箱即用自定义路由代码 这个包装到代码片段或者 github 直接供开发者使用


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有