要实现自定义菜单栏的交互效果,包括触摸滑动、平移和根据条件的显示隐藏等,滑动大于40%或速度大于500时展示或隐藏菜单栏。
您需要进行一系列的操作和设置,以下是实现这一效果的步骤:
状态定义:
首先,定义了一系列的状态变量,如 @State menuMoveX
用于记录菜单栏的平移位置,@State startX
和 @State endX
用于记录触摸事件在 x 轴的起始点和终点,@State z
用于控制主页面的放缩效果,@State flag
用于判断是否满足菜单栏全部平移出来的条件。
返回键处理:
当返回键被按下时,如果菜单栏处于完全展示状态(即 this.menuMoveX == '100%'
),则先将菜单栏平移回去,通过动画效果将 this.menuMoveX
置为 '0%'
,并设置 this.z = 0
,然后阻止返回键的默认行为(即不退出应用),返回 true
。否则,允许返回键的默认行为,返回 false
。
触摸事件处理:
在触摸事件中,根据不同的触摸类型(如按下、移动、抬起)进行相应的处理。
在按下时,重置条件,标记菜单栏未完全平移出来。
在移动时,计算菜单栏的平移位置,并根据平移位置调整主页面的放缩效果。
在抬起时,根据平移位置判断是否满足菜单栏完全平移出来的条件,如果满足则进行相应的动画处理和状态更新,否则也进行相应的动画处理将菜单栏复位。
滑动事件处理:
通过滑动事件获取滑动速度,当不满足菜单栏完全平移出来的条件时,根据速度和起始点、终点的位置关系决定菜单栏的最终位置,并进行相应的动画处理和状态更新。
示例代码:
@Entry
@Component
struct Index {
@State menuMoveX:string = '0' //菜单栏的平移
@State startX:number = 0 //触摸事件x轴的起始点
@State endX:number = 0 //触摸事件x轴的终点 判断向左或向右
@State z:number = 0 //控制主页面的放缩效果
@State flag:boolean = false //判断是否满足菜单栏全部平移出来的条件
// 返回键 菜单栏展示时 不退出 先将菜单栏平移回去
onBackPress() {
if(this.menuMoveX=='100%'){
animateTo({duration:300},()=>{
this.menuMoveX='0%';this.z=0})
return true
}
return false
}
build() {
Stack(){
Column({space:20}){
Text('我是主页面')
.fontSize(25)
}
.alignItems(HorizontalAlign.End)
.translate({z:this.z})
.animation({duration:200})
.backgroundColor('#ffaeaeae')
.height('100%')
.width('100%')
.onTouch((event:TouchEvent)=>{
if(event.type === TouchType.Down){
// 重置条件 刚点击时为不满足菜单栏全部平移出来的条件
this.flag = false
this.startX = event.touches[0].displayX
}
if(event.type === TouchType.Move){
this.menuMoveX = ((event.touches[0].displayX-this.startX)/3.6).toString()+'%'
// 防止反向滑动导致主页面的放缩异常
this.z = parseFloat(this.menuMoveX)>0 ? parseFloat(this.menuMoveX):0
}
if(event.type === TouchType.Up){
this.endX = event.touches[0].displayX
if(parseFloat(this.menuMoveX)>40){
// 当移动大于40%时 满足菜单栏全部平移出来的条件
this.flag = true
animateTo({duration:300},()=>{this.menuMoveX='100%'})
this.z = parseFloat(this.menuMoveX)
}else{
animateTo({duration:300},()=>{this.menuMoveX='0%'})
this.z = parseFloat(this.menuMoveX)
}
}
})
// 滑动事件 用于获取滑动的速度
.gesture(
SwipeGesture({ direction: SwipeDirection.Horizontal })
.onAction((event: GestureEvent) => {
//满足菜单栏全部平移出来的条件 时 无需考虑速度
if(!this.flag){
if (event && event.speed>=500 && this.endX>this.startX) {
animateTo({duration:300},()=>{this.menuMoveX='100%'})
this.z = parseFloat(this.menuMoveX)
}else{
animateTo({duration:300},()=>{this.menuMoveX='0%'})
this.z = parseFloat(this.menuMoveX)
}
}
})
)
Column(){
Text('我是菜单栏')
}
.backgroundColor(Color.Pink)
.height('100%')
.width('100%')
.translate({x:'-100%'})
.offset({x:this.menuMoveX})
.onTouch((event:TouchEvent)=>{
if(event.type === TouchType.Down){
// 重置条件 刚点击时为不满足菜单栏全部平移出来的条件
this.flag = false
this.startX = event.touches[0].displayX
}
if(event.type === TouchType.Move){
// 阻止反向滑动的平移
if(event.touches[0].displayX <= this.startX){
this.menuMoveX = ((event.touches[0].displayX-this.startX)/3.6+100).toString()+'%'
this.z = parseFloat(this.menuMoveX)
}
}
if(event.type === TouchType.Up){
if(parseFloat(this.menuMoveX)<60){
// 当移动大于40%时 满足菜单栏全部平移出来的条件
this.flag = true
animateTo({duration:300},()=>{this.menuMoveX='0%'})
this.z = parseFloat(this.menuMoveX)
}else{
animateTo({duration:300},()=>{this.menuMoveX='100%'})
this.z = parseFloat(this.menuMoveX)
}
}
})
// 滑动事件 用于获取滑动的速度
.gesture(
SwipeGesture({ direction: SwipeDirection.Horizontal })
.onAction((event: GestureEvent) => {
//满足菜单栏全部平移出来的条件 时 无需考虑速度
if(!this.flag){
if (event && event.speed>=500 && this.endX<this.startX) {
animateTo({duration:300},()=>{this.menuMoveX='0%'})
this.z = parseFloat(this.menuMoveX)
}else{
animateTo({duration:300},()=>{this.menuMoveX='100%'})
this.z = parseFloat(this.menuMoveX)
}
}
})
)
}
.backgroundColor(Color.White)
}
}
注意事项:
event.touches[0].displayX貌似在预览器中无法获取到,尝试时请使用模拟器。在实际开发中,需要根据具体的需求和界面布局进行微调,以确保菜单栏的交互效果流畅自然。同时,不同的设备和屏幕尺寸可能会对触摸和滑动的响应产生影响,需要进行充分的测试和优化。另外,对于动画效果的时长和缓动函数的选择,也需要根据用户体验进行适当的调整。
本代码为基于API12,低版本API可能会出现报错。