拖拽式交互
对比使用手势组件前后的操作,小程序半屏手势操作更加灵敏。用户仅需拖拽即可操作整个半屏页面,也能够与滚动组件无缝切换。
实现以上效果的原理主要是判断不同条件下由外层手势组件 pan-gesture-handler 还是内层滚动组件 scroll-view 响应的问题。
当用户在半屏区域向下滑动时,
如果滚动条处于顶部,外层组件 pan-gesture-handler 响应拖动,半屏页面向下拖动至关闭
如果滚动条不处于顶部,内层组件 scroll-view 响应滑动,半屏的内容列表向下滑动至最底端
当用户在半屏区域向上滑动时,
如果半屏页面不完全打开,外层组件 pan-gesture-handler 响应拖动,半屏页面向上拖动至完全展示半屏
如果半屏页面完全打开,内层组件 scroll-view 响应滑动,半屏的内容列表向上滑动至最顶端
想要实现半屏页面的灵活操作,仅需新增调用 pan-gesture-handler (用于拖动触发)以及 vertical-drag-gesture-handler (用于纵向滑动触发)2个 手势组件 即可快速实现。
编写 WXML 代码过程中,开发者主要使用 4 个核心属性,例如 on-gesture-event 等。编写 JS 代码过程中,开发者需要调用 worklet 函数,改变半屏的状态值,使得小程序实现类似原生动画的体验。
拖拽式交互如此丝滑,你可以通过下面方式应用最佳实践:
通过微信开发者工具导入 代码片段(注意:使用 PC 端浏览器打开链接)
点击 查看完整代码
<!-- page.wxml -->
<pan-gesture-handler id="pan"
should-response-on-move="shouldPanResponse"
simultaneous-handlers="{{['scroll']}}"
on-gesture-event="handlePan">
<!-- 代理 scroll-view 的手势节点 vertical-drag-gesture-handler -->
<vertical-drag-gesture-handler id="scroll"
native-view="scroll-view"
should-response-on-move="shouldScrollViewResponse"
simultaneous-handlers="{{['pan']}}">
<scroll-view type="list" scroll-y> ... </scroll-view>
</vertical-drag-gesture-handler>
</pan-gesture-handler>
// page.js
// shared 创建的变量为共享变量,可在 UI 线程和 JS 线程间同步
this.transY = wx.worklet.shared(1000)
this.scrollTop = wx.worklet.shared(0)
this.startPan = wx.worklet.shared(true)
// shouldPanResponse 和 shouldScrollViewResponse 用于 pan 手势和 scroll-view 滚动手势的协商
shouldPanResponse() {
'worklet'
return this.startPan.value
},
shouldScrollViewResponse(pointerEvent) {
'worklet'
// transY > 0 说明 pan 手势在移动半屏,此时 scroll-view 滚动不应生效
if (this.transY.value > 0) return false
const scrollTop = this.scrollTop.value
const { deltaY } = pointerEvent
// deltaY > 0 是往上滚动,scrollTop <= 0 是滚动到顶部边界,此时 pan 开始生效,scroll-view 滚动不生效
const result = scrollTop <= 0 && deltaY > 0
this.startPan.value = result
return !result
},
// pan 手势处理
handlePan(gestureEvent) {
'worklet'
if (gestureEvent.state === GestureState.ACTIVE) {
const curPosition = this.transY.value
const destination = Math.max(0, curPosition + gestureEvent.deltaY)
// 改变半屏的位置
this.transY.value = destination
}
// 其他手势状态的处理,如滚动结束时计算半屏处于打开还是关闭的状态
}
分段式交互
针对信息同时显示的需求,半屏页面是否有更好的交互?例如地图小程序开发者希望在展示多段导航信息的同时不影响用户查看当前定位。
分段式交互能够满足上述多信息显示的需求。用户通过自由拖拽和位置停留,实现分段式半屏效果。
分段式半屏的开发也很方便,在拖拽式交互的基础上增加当前半屏位置判断以响应分段式半屏还是内容列表。
既然分段式半屏如此方便,你可以通过下面方式快速应用:
通过微信开发者工具导入 代码片段(注意:使用 PC 端浏览器打开链接)
点击 查看完整代码
// page.js
// 设置 map scale
// 运行在 JS 线程
setMapScale(scale) {
this.setData({ scale })
},
// worklet 函数,运行在 UI 线程
scrollTo(toValue) {
'worklet'
let scale = 18
if (toValue > screenHeight / 2) {
scale = 16
}
// 从 UI 线程调回 JS 线程
wx.worklet.runOnJS(this.setMapScale.bind(this))(scale)
this.transY.value = timing(toValue, { duration: 200 })
},
// 处理拖动半屏的手势
handlePan(gestureEvent) {
'worklet'
// 滚动半屏的位置
if (gestureEvent.state === GestureState.ACTIVE) {
// deltaY < 0,往上滑动
this.upward.value = gestureEvent.deltaY < 0
// 当前半屏位置
const curPosition = this.transY.value
// 只能在 [statusBarHeight, screenHeight] 之间移动
const destination = clamp(curPosition + gestureEvent.deltaY, statusBarHeight, screenHeight)
if (curPosition === destination) return
// 改变 transY,来改变半屏的位置
this.transY.value = destination
}
if (gestureEvent.state === GestureState.END || gestureEvent.state === GestureState.CANCELLED) {
if (this.transY.value <= screenHeight / 2) {
// 在上面的位置
if (this.upward.value) {
this.scrollTo(statusBarHeight)
} else {
this.scrollTo(screenHeight / 2)
}
} else if (this.transY.value > screenHeight / 2 && this.transY.value <= this.initTransY.value) {
// 在中间位置的时候
if (this.upward.value) {
this.scrollTo(screenHeight / 2)
} else {
this.scrollTo(this.initTransY.value)
}
} else {
// 在最下面的位置
this.scrollTo(this.initTransY.value)
}
}
},
页面转场交互
针对更丰富的半屏内容,例如小程序助手需要展开多位成员的申请详情,转场半屏交互能够实现更顺滑的交互效果,给予用户更自然、友好的体验。
通过 自定义路由 与手势组件的结合,开发者不仅能够实现以上页面转场交互的效果,还能够实现更多类原生的页面切换效果。后续的文章将会详细分享自定义路由的最佳实践,敬请关注!
小程序手势组件助力开发者更高效地实现类原生交互的体验,对用户操作更友好。以半屏页面为例的场景通过手势操作实现丝滑、灵敏的效果,实现更丰富的应用。
更多接口相关问题,可点击 微信开放社区 发帖反馈,技术专员将为大家解答及进行深度交流。