甲低有什么危害| 什么水是碱性水| 大便特别臭是什么原因| 什么是借读生| 屁股痛是什么原因| 深圳有什么好玩的地方| 脚气是什么菌感染| 13颗珠子的手串什么意思| 三七有什么功效和作用| 附睾炎吃什么药| 梦到自己被蛇咬是什么意思| 什么叫消融手术| 木字多一撇是什么字| 貘是什么动物| 牛肉和什么菜包饺子好吃| 弄虚作假是什么生肖| aigner是什么牌子| 暖手宝里面是什么| 邕是什么意思| 属马与什么属相最配| 酸儿辣女什么意思| 白薯是什么| 降调是什么意思| 血稠是什么原因引起的| 女上位是什么意思| 什么植物和动物像鸡| 癞蛤蟆吃什么| 为什么小便会带血| 5公里25分钟什么水平| 喉咙沙哑吃什么药| 广西有什么特产| 什么叫人彘| 茯苓生长在什么地方| 静待花开的前一句是什么| 右肺中叶少许纤维灶是什么意思| 村支部书记是什么级别| 鸡的守护神是什么菩萨| 干什么挣钱快| 攻击是什么意思| 吃什么增强抵抗力和免疫力| 肾阴虚的症状吃什么药| 红颜知己什么意思| 副县长什么级别| 91年五行属什么| 牙疼吃什么药最管用| 甲状腺4a类什么意思| 共度良宵是什么意思| 双侧中耳乳突炎是什么意思| 为什么做b超要憋尿| 傧相是什么意思| 小厮是什么意思| 水滴鱼长什么样子| 心凉是什么意思| 叶黄素对眼睛有什么功效| 大姨妈来了吃什么水果好| 绿色痰是什么原因| 眼睛干涩有异物感用什么眼药水| 拉拉是什么意思| 白垩纪是什么意思| 10.28是什么星座| 机智如你是什么意思| 嘿是什么意思| 牛牛是什么意思| 姗字五行属什么| 外阴炎什么症状| 木吉他什么牌子比较好| 蟑螂长什么样| 上腹部饱胀是什么原因| 醋酸泼尼松片治什么病| 耳朵不舒服是什么原因| 吃什么排肝毒最快| 蜘蛛代表什么生肖| 独在异乡为异客是什么节日| 812是什么意思| 糯米粉可以做什么| 手掌小鱼际发红是什么原因| 低脂是什么意思| 皮肤黑的人穿什么颜色的衣服显白| 肉桂是什么味道| 网球肘用什么方法能彻底治好呢| 尿毒症吃什么最好| 李耳为什么叫老子| 人体缺少蛋白质会有什么症状| 胆囊结石有什么影响| 麦粒肿滴什么眼药水| 橘络的功效与作用是什么| 太形象了是什么意思| nba什么时候开始| 男性尿出血什么原因| 出虚恭什么意思| 黑色裤子配什么颜色t恤| 上海有什么好玩的地方| 梦见豹子是什么预兆| 大腿粗是什么原因导致的| 什么叫市级以上医院| 吃什么会影响验孕棒检验结果| 魂不守舍什么意思| 宫颈疼是什么原因| 藿香正气水有什么用| 骨质增生吃什么药最好| 知否知否应是绿肥红瘦什么意思| 脸上长闭口是什么原因导致的| 凌晨三点醒是什么原因| 微信为什么发不了视频| 神气活现是什么意思| 胡萝卜不能和什么一起吃| camper是什么牌子| 梦见去看病是什么意思| 尿酸高是为什么| 干眼症是什么原因引起的| 过度是什么意思| h的大写字母是什么| 吃毛蛋有什么好处| 立冬和冬至什么区别| 6月1号什么星座| 什么样子| 春雨绵绵是什么生肖| 福利姬什么意思| 舌头痛什么原因| 7月10号什么星座| 吃燕麦片有什么好处| 黄色裤子配什么上衣好看| 寒衣节是什么意思| 乳头瘤病毒是什么意思| 奶粉罐可以做什么手工| 小便尿不出来什么原因| 第二次世界大战是什么时候| 北京户口有什么好处| 为什么会得带状疱疹| 发烧应该挂什么科| c2是什么车型| ml 什么意思| 胃溃疡是什么症状| 喝水呛咳是什么原因| 老鼠最怕什么| 闪失是什么意思| 阴虱用什么药| 宝宝爱出汗是什么原因| 葛根粉是什么| movefree是什么药| 受精卵着床失败有什么症状| 08属什么生肖| 什么是膳食纤维| 什么的长江| 幼儿园中班学什么| 老是放屁吃什么药| 女生经常手淫有什么危害| br什么意思| 孩子流黄鼻涕是什么原因| 攒局什么意思| 孕妇什么时候开始补钙| 摆拍是什么意思| cg动画是什么意思| 男人更年期吃什么药| 萎缩性胃炎吃什么水果好| 10月12号是什么星座| 凝固酶阳性是什么意思| 新生儿嘴唇发紫是什么原因| 耳鸣什么原因引起的| 微凉是什么意思| 肝岛是什么意思| 什么病可以申请低保| 生意盎然什么意思| nt值代表什么| 4月14日是什么星座| 四叶草的寓意是什么| 海扶治疗是什么| 什么叫八字| 违拗是什么意思| 什么是绿茶| 什么东西越洗越脏脑筋急转弯| 低压高吃什么药好| 室性早搏吃什么药| 身份证后四位是什么意思| 骨折吃什么好的快| 凤凰男什么意思| 皮肤黄是什么原因引起的| 立夏节吃什么| 贫血严重会导致什么后果| 吃什么提高免疫力最快| 聪明绝顶是什么意思| 公众号是什么意思| 213什么星座| 空腹喝可乐有什么危害| 婴儿湿疹用什么药膏| 尖湿锐吃什么药最好| 什么的笑| 朱元璋为什么杀徐达| qid医学上是什么意思| 喝什么泡水降血压最好| 做梦梦到地震预示着什么| 向日葵代表什么| 女人更年期有什么症状| 为什么微信附近的人看不到我| 身份证号码的数字代表什么意义| cst是什么时间| 鱼石脂是什么| 人为什么会得阑尾炎| 晚上左眼皮跳预示什么| 捕风捉影是什么意思| 耵聍栓塞是什么意思| 属马的本命佛是什么佛| 毛豆子炒什么好吃| 空腹胰岛素低说明什么| 乔其纱是什么面料| 路上行人匆匆过是什么歌| roma是什么意思| 发烧39度吃什么药| 落地生根是什么生肖| 三头六臂是什么生肖| 凌晨12点是什么时辰| camus是什么酒| 治标不治本是什么意思| 金玉良缘什么意思| 廿是什么意思| 评头论足什么意思| 嗓子哑吃什么药| 吃桃有什么好处| 头晕做什么检查| 运动后出汗多是什么原因| 检查甲状腺挂什么科| 夏天为什么不能喝中药| 神经纤维瘤是什么病| 连号的钱为什么不能花| 混社会的人一般干什么| 眩晕症是什么原因造成的| 阿司匹林肠溶片什么时候吃| 四个月宝宝可以吃什么辅食| nmr是什么意思| 结婚27年是什么婚| 美国为什么打朝鲜| 胰腺炎为什么喝水就死| 羽毛球拍u是什么意思| 白细胞十十是什么意思| 睡觉盗汗是什么原因| 蝙蝠飞到家里是什么预兆| 阴道炎什么症状| 竖心旁与什么有关| 脚有酸臭味是什么原因| 双肾尿酸盐结晶是什么意思| 失眠为什么开奥氮平片| 大力念什么| 岁寒三友是什么意思| 梨子什么时候成熟| 线粒体是什么| 空调风扇不转是什么原因| 牛肉饺子馅配什么蔬菜好吃| 乙型肝炎e抗体阳性是什么意思| pm是什么单位| ppd是什么| 拔了牙吃什么消炎药| 后背沉重感是什么原因引起的| hpv52高危阳性是什么意思| 真菌阳性是什么意思| 看门神是什么生肖| 中宫是什么意思| 指腹脱皮是什么原因| 感冒吃什么水果好| 7点到9点是什么时辰| 骨骼肌是什么意思| wpw综合症是什么意思| 总胆固醇低是什么原因| 梦见买楼房有什么预兆| 屠苏是什么意思| 百度

空间大配置高 两款7万级的紧凑级SUV对比

百度 用白话一点的话来说,上海光源就相当于一个超级显微镜集群,能够帮助科研人员看清一个病毒结构、材料的微观构造和特性。

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

Skyline 渲染引擎下,页面有两种渲染模式: WebViewSkyline,它们通过页面配置中的 renderer 字段进行区分。在连续的 Skyline 页面间跳转时,可实现自定义路由效果。

# 效果展示

下方为半屏页面效果,点击可查看更多 Skyline 示例

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

# 使用方法

建议先阅读完 worklet 动画手势系统 两个章节,它们是自定义路由的基础内容。

# 接口定义

自定义路由相关的接口

type AddRouteBuilder = (routeType: string, routeBuilder: CustomRouteBuilder) => void

type CustomRouteBuilder = (routeContext: CustomRouteContext, routeOptions: Record<string, any>) => CustomRouteConfig

interface SharedValue<T> {
  value: T;
}

interface CustomRouteContext {
  // 动画控制器,影响推入页面的进入和退出过渡效果
  primaryAnimation: SharedValue<number>
  // 动画控制器状态
  primaryAnimationStatus: SharedValue<number>
  // 动画控制器,影响栈顶页面的推出过渡效果
  secondaryAnimation: SharedValue<number>
  // 动画控制器状态
  secondaryAnimationStatus: SharedValue<number>
  // 当前路由进度由手势控制
  userGestureInProgress: SharedValue<number>
  // 手势开始控制路由
  startUserGesture: () => void
  // 手势不再控制路由
  stopUserGesture: () => void
  // 返回上一级,效果同 wx.navigateBack
  didPop: () => void
}

interface CustomRouteConfig {
  // 下一个页面推入后,不显示前一个页面
  opaque?: boolean;
  // 是否保持前一个页面状态
  maintainState?: boolean;
  // 页面推入动画时长,单位 ms
  transitionDuration?: number;
  // 页面推出动画时长,单位 ms
  reverseTransitionDuration?: number;
  // 遮罩层背景色,支持 rgba() 和 #RRGGBBAA 写法
  barrierColor?: string;
  // 点击遮罩层返回上一页
  barrierDismissible?: boolean;
  // 无障碍语义
  barrierLabel?: string;  
  // 是否与下一个页面联动,决定当前页 secondaryAnimation 是否生效
  canTransitionTo?: boolean;
  // 是否与前一个页面联动,决定前一个页 secondaryAnimation 是否生效
  canTransitionFrom?: boolean;
  // 处理当前页的进入/退出动画,返回 StyleObject
  handlePrimaryAnimation?: RouteAnimationHandler;
  // 处理当前页的压入/压出动画,返回 StyleObject
  handleSecondaryAnimation?: RouteAnimationHandler;
  // 处理上一级页面的压入/压出动画,返回 StyleObject 基础库 <3.0.0> 起支持
  handlePreviousPageAnimation?: RouteAnimationHandler;
  // 页面进入时是否采用 snapshot 模式优化动画性能 基础库 <3.2.0> 起支持
  allowEnterRouteSnapshotting?: boolean
  // 页面退出时是否采用 snapshot 模式优化动画性能 基础库 <3.2.0> 起支持
  allowExitRouteSnapshotting?: boolean
  // 右滑返回时,可拖动范围是否撑满屏幕,基础库 <3.2.0> 起支持,常用于半屏弹窗
  fullscreenDrag?: boolean
  // 返回手势方向 基础库 <3.4.0> 起支持
  popGestureDirection?: 'horizontal' | 'vertical' | 'multi'
}

type RouteAnimationHandler = () => { [key: string] : any}

# 默认路由配置

const defaultCustomRouteConfig = {
  opaque: true,
  maintainState: true,
  transitionDuration: 300,
  reverseTransitionDuration: 300,
  barrierColor: '',
  barrierDismissible: false,
  barrierLabel: '',
  canTransitionTo: true,
  canTransitionFrom: true,
  allowEnterRouteSnapshotting: false,
  allowExitRouteSnapshotting: false,
  fullscreenDrag: false,
  popGestureDirection: 'horizontal'
}

# 示例模板

以下是注册自定义路由的一份示例模板(未添加手势处理部分),完整实现半屏路由效果见示例代码。

const customRouteBuiler = (routeContext: CustomRouteContext) : CustomRouteConfig => {
  const {
    primaryAnimation,
    secondaryAnimation,
    userGestureInProgress
  } = routeContext

  const handlePrimaryAnimation: RouteAnimationHandler = () => {
    'worklet'
    let t = primaryAnimation.value
    if (!userGestureInProgress.value) {
      // select another curve, t = xxx
    }
    // StyleObject
    return {}
  }
  
  const handleSecondaryAnimation: RouteAnimationHandler = () => {
    'worklet'
    let t = secondaryAnimation.value
    if (!userGestureInProgress.value) {
      // select another curve, t = xxx
    }
    // StyleObject
    return {}
  }

  return {
    opaque: true,
    handlePrimaryAnimation,
    handleSecondaryAnimation
  }
}

// 在页面跳转前定义好 routeBuilder
wx.router.addRouteBuilder('customRoute', customRouteBuiler)

// 跳转新页面时,指定对应的 routeType
wx.navigateTo({
  url: 'xxxx',
  routeType: 'customRoute'
})

# 工作原理

以半屏效果为例,路由前后页面记为 A 页、B 页,一个路由的生命周期中,会经历如下阶段:

  1. push 阶段 :调用 wx.navigateToB 页自底向上弹出,A 页下沉收缩
  2. 手势拖动:在 B 页上下滑动时,路由动画随之变化
  3. pop 阶段 :调用 wx.navigateBackB 页向下关闭,A 恢复原样

细分到每个页面,在上述阶段会有以下动画方式

  1. 进入/退出动画
  2. 压入/压出动画
  3. 手势拖动
  • push 阶段,B 页进行的是进入动画,A 页进行的是压入动画;
  • pop 阶段,B 页进行的是退出动画,A 页进行的是压出动画;

可以看到在路由过程中,前后两个页面动画进行了联动。在自定义路由模式下,我们可以对动画各个阶段的时长、曲线、效果以及是否联动进行自定义,以实现灵活多变的页面专场效果。

# 路由控制器

当打开新页面时,框架会为其创建两个 SharedValue 类型的动画控制器 primaryAnimationsecondaryAnimation,分别控制进入/退出动画和压入/压出动画。

页面的进入和退出可指定不同的时长,但进度变化始终在 0~1 之间。仍以半屏效果为例,路由前后页面记为 A 页、B 页。

# push 阶段

  1. B 页对应的 primaryAnimation0 -> 1 变化,做进入动画
  2. A 页对应的 secondaryAnimation0 -> 1 变化,做压入动画

# pop 阶段

  1. B 页对应的 primaryAnimation1 -> 0 变化,做退出动画
  2. A 页对应的 secondaryAnimation1 -> 0 变化,做压出动画

其中,AsecondaryAnimation 的值始终与 BprimaryAnimation 的值同步变化。

通常页面的进入和退出可能采用不同的动画曲线,可通过对应的状态变量 primaryAnimationStatussecondaryAnimationStatus 来区分当前处于哪一阶段,ts 定义如下

enum AnimationStatus {
  // 动画停在起点
  dismissed = 0,
  // 动画从起点向终点进行
  forward = 1,
  // 动画从终点向起点进行
  reverse = 2,
  // 动画停在终点
  completed = 3,
}

primaryAnimationStatus 为例,页面进入和退出过程中变化情况如下

  1. push 阶段:dismissed -> forward -> completed
  2. pop 阶段:completed -> reverse -> dismissed

# 路由手势

在页面推入后,除了调用 wx.navigateBack 接口返回上一级外,还可以通过手势来处理,例如 iOS 上常见的右滑返回。自定义路由模式下,开发者可根据不同的页面转场效果,来选取所需的退出方式,如半屏效果可采用下滑返回。关于手势监听的内容,可参考 手势系统 一章,路由手势仅是在其基础上,补充了几个路由相关的接口。

startUserGesturestopUserGesture 两个函数总是成对调用的,startUserGesture 调用后 userGestureInProgress 的值会加 1

当开发者自行修改 primaryAnimation 的值来控制路由进度的时候,就需要调用这两个接口。由于手势拖动过程中通常采用不同的动画曲线,可通过 userGestureInProgress 值进行判断。

当手势处理后确定需要返回上一级页面时,调用 didPop 接口,作用等同 wx.navigateBack

# 路由联动

路由动画过程中,默认前后两个页面是一起联动的,可通过配置项关闭。

  1. canTransitionTo:是否与下一个页面联动,栈顶页面该属性置为 false ,推入下一页面时,则栈顶页面始终不动
  2. canTransitionFrom:是否与前一个页面联动,新推入页面该属性置为 false,则栈顶页面始终不动

# 路由上下文对象

由示例模版可见,自定义路由的动画效果就是根据 CustomRouteContext 上下文对象上的路由控制器,编写适当的动画更新函数来实现。

CustomRouteContext 上下文对象还可在页面/自定义组件中通过 wx.router.getRouteContext(this) 读取,进而在手势处理过程中访问,通过对 primaryAnimation 值的改写实现页面手势返回。

小技巧:可在 CustomRouteContext 对象上添加一些私有属性,在页面中进行读取/修改。

# 多类型路由跳转

考虑这样的场景,从页面 A 可能跳转到 B 页和 C 页,但具有不同的路由动画

  1. A -> B 时,希望实现半屏效果,A 需要下沉收缩
  2. A -> C 时,希望采用普通路由,A 需要向左移动

跳转下一级页面时的动画由 handleSecondaryAnimation 控制,这样就需要在定义 ACustomRouteBuilder 时考虑所有的路由类型,实现较为繁琐。

基础库 3.0.0 版本起,自定义路由新增 handlePreviousPageAnimation 接口,用于控制上一级页面的压入/压出动画。

const customRouteBuiler = (routeContext: CustomRouteContext) : CustomRouteConfig => {
  const { primaryAnimation } = routeContext

  const handlePrimaryAnimation: RouteAnimationHandler = () => {
    'worklet'
    let t = primaryAnimation.value
    // 控制当前页的进入和退出
  }
  
  const handlePreviousPageAnimation: RouteAnimationHandler = () => {
    'worklet'
    let t = primaryAnimation.value
    // 控制上一级页面的压入和退出
  }

  return {
    handlePrimaryAnimation,
    handlePreviousPageAnimation
  }
}

A 跳转到 B 时, AsecondaryAnimation 的值始终与 BprimaryAnimation 的值同步变化。

我们可以在定义 BCustomRouteBulder 时,通过 primaryAnimation 得知当前路由进度,handlePreviousPageAnimation 返回的 StyleObject 会作用于上一级页面。

同时也不再需要提前声明 A 为自定义路由,在此之前 A 跳转 B 希望实现半屏效果时,A 也必须定义为自定义路由。

完整的示例可参考如下代码,借助 handlePreviousPageAnimation 可去掉对 secondaryAnimation 的依赖,简化代码逻辑。

在开发者工具中预览效果

# 实际案例

下面以半屏效果为例,讲解自定义路由的具体实现过程,完整代码见示例代码。

路由前后页面分别记为 A 页和 B 页,需要分别为其注册自定义路由。未注册任何自定义路由效果时,新打开的页面 B 会立即覆盖显示在 A 页上。

# Step-1 页面进入动画

我们先分别简单实现 首页 -> A 页 -> B 页的进入动画,再一步步进行完善。

对于 A 页面,进入方式为自右向左,通过 transform 平移实现。

function ScaleTransitionRouteBuilder(customRouteContext) {
  const {
    primaryAnimation
  } = customRouteContext

  const handlePrimaryAnimation = () => {
    'worklet'
    let t = primaryAnimation.value
    const transX = windowWidth * (1 - t)
    return {
      transform: `translateX(${transX}px)`,
    }
  }
  return {
    handlePrimaryAnimation
  }
}

对于 B 页面,进入方式为自底向上,也是通过 transform 平移实现,但需要对页面大小、圆角进行修改。

const HalfScreenDialogRouteBuilder = (customRouteContext) => {
  const {
    primaryAnimation,
  } = customRouteContext

  const handlePrimaryAnimation = () => {
    'worklet'
    let t = primaryAnimation.value
    // 距离顶部边距因子
    const topDistance = 0.12
    // 距离顶部边距
    const marginTop = topDistance * screenHeight
    // 半屏页面大小
    const pageHeight = (1 - topDistance) * screenHeight
    // 自底向上显示页面
    const transY = pageHeight * (1 - t)
    return {
      overflow: 'hidden',
      borderRadius: '10px',
      marginTop: `${marginTop}px`,
      height: `${pageHeight}px`,
      transform: `translateY(${transY}px)`,
    }
  }

  return {
    handlePrimaryAnimation,
  }
}

页面跳转效果如下,可以看到由于采用线性曲线(未对 t 做任何变换),动画有些呆板,同时未区分进入/退出动画。在 B 页完全进入后,A 页变的不可见。

# Step-2 自定义动画曲线

B 页为例,根据 AnimationStatus 值,采用不同的动画曲线,同时设置 opaquefalse,使得路由动画完成后仍显示 A 页面。

const { Easing, derived } = wx.workelt

const Curves = {
  linearToEaseOut: Easing.cubicBezier(0.35, 0.91, 0.33, 0.97),
  easeInToLinear: Easing.cubicBezier(0.67, 0.03, 0.65, 0.09),
  fastOutSlowIn: Easing.cubicBezier(0.4, 0.0, 0.2, 1.0),
  fastLinearToSlowEaseIn: Easing.cubicBezier(0.18, 1.0, 0.04, 1.0),
}

function CurveAnimation({ animation, animationStatus, curve,reverseCurve }) {
  return derived(() => {
    'worklet'
    const useForwardCurve = !reverseCurve || animationStatus.value !== AnimationStatus.reverse
    const activeCurve = useForwardCurve ? curve : reverseCurve
    const t = animation.value
    if (!activeCurve) return t
    if (t === 0 || t === 1) return t
    return activeCurve(t)
  })
}
const HalfScreenDialogRouteBuilder = (customRouteContext) => {
  const {
    primaryAnimation,
    primaryAnimationStatus,
  } = customRouteContext

  // 1. 页面进入时,采用 Curves.linearToEaseOut 曲线
  // 2. 页面退出时,采用 Curves.easeInToLinear 曲线
  const _curvePrimaryAnimation = CurveAnimation({
    animation: primaryAnimation,
    animationStatus: primaryAnimationStatus,
    curve: Curves.linearToEaseOut,
    reverseCurve: Curves.easeInToLinear,
  })

  const handlePrimaryAnimation = () => {
    'worklet'
    let t = _curvePrimaryAnimation.value
    ... // 其余内容等上面的代码一致
  }

  return {
    opaque: false,
    handlePrimaryAnimation,
  }
}

这里的区别仅在于,当前的进度不再直接读取 primaryAnimation 的值。封装的 CurveAnimation 函数会根据 AnimationStatus 判断是处于进入还是退出状态,从而选择不同的动画曲线。框架提供了多种曲线类型,可进一步参考 worklet.Easing。改进后的页面转场效果如下

# Step-3 页面联动效果

B 页进入时,A 页作压入动画,由 secondaryAnimation 控制。接下来,我们为其添加下沉效果,实现和 B 页的联动。

function ScaleTransitionRouteBuilder(customRouteContext) {
  const {
    primaryAnimation
  } = customRouteContext

  const handlePrimaryAnimation = () => {
    'worklet'
    ...
  }

  const _curveSecondaryAnimation = CurveAnimation({
    animation: secondaryAnimation,
    animationStatus: secondaryAnimationStatus,
    curve: Curves.fastOutSlowIn,
  })

  const handleSecondaryAnimation = () => {
    'worklet'
    let t = _curveSecondaryAnimation.value
    // 页面缩放大小
    const scale = 0.08
    // 距离顶部边距因子
    const topDistance = 0.1
    // 估算的偏移量
    const transY = screenHeight * (topDistance - 0.5 * scale) * t 
    return {
      overflow: 'hidden',
      borderRadius: `${ 12 * t }px`,
      transform: `translateY(${transY}px) scale(${ 1 - scale * t })`,
    }
  }

  return {
    handlePrimaryAnimation,
    handleSecondaryAnimation
  }
}

通过对 A 页作 scaletranslate 变换实现下沉效果。AsecondaryAnimation 的值始终与 BprimaryAnimation 的值保持同步。

页面是否联动还可通过 canTransitionTocanTransitionFrom 两个属性进行配置,可在开发者工具上修改体验。

# Step-4 手势返回

目前动画效果已经基本实现,还需要最后一步,手势返回。对于半屏效果,我们为 A 页添加右滑返回手势,B 页添加下滑返回手势。

以最常见的右滑返回为例,这里只截取松手后的手势处理部分代码,拖动过程实现较为简单,可参考示例代码。

page({
  handleDragEnd(velocity) {
    'worklet';
    const {
        primaryAnimation,
        stopUserGesture,
        didPop
    } = this.customRouteContext;

    let animateForward = false;
    if (Math.abs(velocity) >= 1.0) {
      animateForward = velocity <= 0;
    } else {
      animateForward = primaryAnimation.value > 0.5;
    }
    const t = primaryAnimation.value;
    const animationCurve = Curves.fastLinearToSlowEaseIn;
    if (animateForward) {
        const duration = Math.min(
          Math.floor(lerp(300, 0, t)),
          300,
        );
        primaryAnimation.value = timing(
          1.0, {
            duration,
            easing: animationCurve,
          },
          () => {
            'worklet'
            stopUserGesture();
          },
        );
    } else {
      const duration = Math.floor(lerp(0, 300, t));
      primaryAnimation.value = timing(
        0.0, {
          duration,
          easing: animationCurve,
        },
        () => {
          'worklet'
          stopUserGesture();
          didPop();
        },
      );
    }
  },
})

首先根据松手时的速度和位置,决定是否要真正返回上一级。

  1. 向右滑动且速度大于 1
  2. 或者速度较小时,已拖动超过屏幕 1/2

满足以上条件时,确定返回。通过 timing 接口,为 primaryAnimation 添加过渡动画,使其变化到 0,最后调用 didPop 。否则使其变化到 1,恢复到拖动前的状态。

这里需要注意的是,当需要对 primaryAnimation 值手动修改,自由掌控其过渡方式时,才需要调用 startUserGesturestopUserGesture 接口。

右滑手势已经在示例代码中封装成 swipe-back 组件,开发者可直接使用。下滑手势返回逻辑基本一致,仅一些数值上略有差异。

最后的实现效果如图

# 设置页面透明

一些自定义路由效果下,需要实现页面透明背景,这里对 Skylinewebview 模式下背景色的层级关系进行说明。

# 自定义路由下的页面背景色

Skyline 模式下使用自定义路由方式跳转页面,页面背景色有如下几层

  1. 页面背景色:可通过 page 选择器在 wxss 中定义,默认为白色
  2. 页面容器背景色:可在页面 json 文件中通过 backgroundColorContent 属性定义,支持 #RRGGBBAA 写法,默认白色
  3. 自定义路由容器背景色,由路由配置项中返回的 StyleObject 控制,默认透明
  4. 控制是否显示前一个页面,由路由配置项中的 opaque 字段控制,默认不显示

当需要设置下一个页面渐显进入时,可简单设置

  1. 页面背景色透明: page { background-color: transparent; }
  2. 页面容器背景色透明: backgroundColorContent: "#ffffff00"

查看自定义路由页面渐显示例

# webview 下的页面背景色

对比看下,webview 模式下的页面背景色

  1. 页面背景色:可通过 page 选择器在 wxss 中定义,默认为透明
  2. 页面容器背景色:可在页面 json 文件中通过 backgroundColorContent 属性定义,支持 #RRGGBB 写法,默认白色
  3. 窗口背景色:可通过 wx.setBackgroundColor 接口或页面配置修改,默认为白色

# 示例代码

在开发者工具中预览效果

吃什么皮肤白的最快 老是嗜睡是什么原因 多西他赛是什么药 月子吃什么 贵姓是什么意思
什么是电解质饮料 芦荟有什么功效 外阴瘙痒用什么药 幽门螺旋杆菌感染吃什么药 为什么女的会流水怎么回事
河南有什么特色美食 小分子肽有什么作用 警察两杠三星是什么级别 什么病可以申请低保 郑声是什么意思
木加一笔变成什么字 三头六臂是什么生肖 骶椎隐裂是什么意思 检查前列腺需要做什么检查 一什么阳光填量词
香菇炒什么菜好吃hcv8jop9ns2r.cn 全脂牛奶是什么意思hcv8jop7ns4r.cn 姓丁的女孩起什么名字好hcv9jop2ns9r.cn 愚不可及是什么意思hcv8jop9ns0r.cn 庚子五行属什么hcv7jop6ns8r.cn
痛风是什么感觉hcv9jop1ns9r.cn 太监是什么hcv9jop5ns8r.cn 肝左叶囊肿是什么意思hcv8jop3ns3r.cn 12月21号是什么星座hcv8jop2ns4r.cn 灰指甲用什么药效果好hcv7jop9ns8r.cn
泌乳素偏高是什么原因hcv7jop7ns0r.cn 山野是什么意思hcv8jop7ns5r.cn 大名是什么意思hcv8jop6ns9r.cn 属鸡的适合干什么行业最赚钱hcv9jop8ns3r.cn 撕脱性骨折是什么意思hcv7jop5ns3r.cn
地委书记是什么级别hcv8jop5ns4r.cn 放纵什么意思hcv9jop7ns4r.cn kinghome是什么牌子hcv7jop7ns3r.cn 曦字五行属什么hcv8jop6ns9r.cn 店招是什么意思hcv9jop4ns9r.cn
百度