HarmonyOS鸿蒙Next中半模态(bindSheet)在最低档如何禁止下滑?

HarmonyOS鸿蒙Next中半模态(bindSheet)在最低档如何禁止下滑? 半模态(bindSheet)如何在最低档位的时候禁止下滑?我想做到向花瓣地图那种最小档位时无法下滑 现在使用半模态的效果是,可以继续往下滑然后在回弹到最小档位

3 回复

【解决方案】

可以使用滑动手势PanGesture事件来自定义半模态弹窗的滑动过程,实现最低挡位时禁止下拉回弹的效果。示例代码如下:

@Entry
@Component
struct SheetTransitionExample {
  @State isShow: boolean = true;
  @State @Watch('watchHeight') sheetHeight: number = 500;
  @State @Watch('watchFlag') sheetFlag: SheetState = SheetState.MID;
  @State changeValue: string = '';
  @State submitValue: string = '';
  controller: SearchController = new SearchController();
  @State positionY: number = 500;

  @Builder
  myBuilder() {
    Column() {
      Search({ value: this.changeValue, placeholder: '搜地点、公交、地铁、城市', controller: this.controller })
        .width('95%')
        .height(40)
        .backgroundColor('#F5F5F5')
        .placeholderColor(Color.Grey)
        .placeholderFont({ size: 14, weight: 400 })
        .textFont({ size: 14, weight: 400 })
        .onSubmit((value: string) => {
          this.submitValue = value;
        })
        .onChange((value: string) => {
          this.changeValue = value;
        })
        .margin(20);
      // 使用if判断渲染刷新组件,半模态状态为TOP+MID时的组件
      if (this.sheetFlag === SheetState.MID || this.sheetFlag === SheetState.TOP) {
        Column() {
          Row() {
            Text('驾车');
            Text('公交地铁');
            Text('步行');
            Text('打车');
            Text('行程规划');
          }
          .justifyContent(FlexAlign.SpaceBetween)
          .width('95%');
        }
        .justifyContent(FlexAlign.Center)
        .height(50)
        .width('90%')
        .margin(20)
        .borderRadius(8)
        .borderWidth(1);

        Column() {
          Row() {
            Text('常用地点');
            Text('设置');
          }
          .justifyContent(FlexAlign.SpaceBetween)
          .alignItems(VerticalAlign.Top)
          .width('95%')
          .margin(20);

          Row() {
            Column() {
              Text('回家');
              Text('点击设置');
            };

            Column() {
              Text('去公司');
              Text('点击设置');
            };
          }
          .justifyContent(FlexAlign.SpaceBetween)
          .width('95%')
          .height(150);
        }
        .width('90%')
        .borderRadius(8)
        .borderWidth(1);

        Text('test test test ')
          .fontSize(40)
          .margin(40);
      }
      // 使用if判断渲染刷新组件,半模态状态为TOP时的组件
      if (this.sheetFlag === SheetState.TOP) {
        Column() {
          Image($r('app.media.background'))
            .height(300)
            .objectFit(ImageFit.Contain);
        }
        .transition(TransitionEffect.OPACITY.animation({ duration: 100 })); // 内容出现、消失的transition动效
      }

    }
    .gesture(
      // direction只涉及竖直方向上的移动,distance值设为1,使滑动更灵敏,避免造成事件错乱。
      PanGesture({ direction: PanDirection.Vertical, distance: 1 })
        .onActionStart(() => {
          console.info('Pan start');
        })
        .onActionUpdate((event: GestureEvent) => {
          // 手势事件偏移量Y,单位为vp,用于PanGesture手势触发场景,从上向下滑动offsetY为正,反之为负。
          console.info(`onActionUpdate offsetY: ${event.offsetY}`);
          // 当滑动时间没持续发生时,半模态高度只能在100-790之间移动
          if (this.sheetHeight >= 100 && this.sheetHeight <= 790) {
            // 当半模态高度为100,再往下滑动时直接返回
            if (this.sheetHeight === 100 && event.offsetY > 0) {
              return;
            }
            // 当半模态高度为790,再往上滑动时直接返回
            if (this.sheetHeight === 790 && event.offsetY < 0) {
              return;
            }
            // 手指持续滑动拖动半模态页面高度时,实时刷新半模态高度sheetHeight
            this.sheetHeight = this.positionY + (-event.offsetY);
          }

        })
        .onActionEnd((event: GestureEvent) => {
          // 快速上滑时
          if (event.offsetY < -10 && event.offsetY > -200) {
            if (this.sheetFlag === SheetState.MID) {
              this.getUIContext().animateTo({
                duration: 300,
                curve: Curve.Friction,
                onFinish: () => {
                  this.sheetFlag = SheetState.TOP;
                }
              }, () => {
                this.sheetHeight = 790;
              });
            } else if (this.sheetFlag === SheetState.BOTTOM) {
              this.getUIContext().animateTo({
                duration: 300,
                curve: Curve.Friction,
                onFinish: () => {
                  this.sheetFlag = SheetState.MID;
                }
              }, () => {
                this.sheetHeight = 500;
              });
            }
          }
          // 快速上滑时
          if (event.offsetY > 10 && event.offsetY < 200) {
            if (this.sheetFlag === SheetState.TOP) {
              this.getUIContext().animateTo({
                duration: 300,
                curve: Curve.Friction,
                onFinish: () => {
                  this.sheetFlag = SheetState.MID;
                }
              }, () => {
                this.sheetHeight = 500;
              });
            } else if (this.sheetFlag === SheetState.MID) {
              this.getUIContext().animateTo({
                duration: 300,
                curve: Curve.Friction,
                onFinish: () => {
                  this.sheetFlag = SheetState.BOTTOM;
                }
              }, () => {
                this.sheetHeight = 100;
              });
            }
          }
          // 拖动手势结束时,半模态靠近哪个状态,就归到哪个状态
          if (this.sheetHeight <= 790 && this.sheetHeight >= 650) {
            this.getUIContext().animateTo({
              duration: 300,
              curve: Curve.Friction,
              onFinish: () => {
                this.sheetFlag = SheetState.TOP;
              }
            }, () => {
              this.sheetHeight = 790;
            });
          } else if (this.sheetHeight < 650 && this.sheetHeight >= 300) {
            this.getUIContext().animateTo({
              duration: 300,
              curve: Curve.Friction,
              onFinish: () => {
                this.sheetFlag = SheetState.MID;
              }
            }, () => {
              this.sheetHeight = 500;
            });
          } else if (this.sheetHeight < 300 && this.sheetHeight >= 100) {
            this.getUIContext().animateTo({
              duration: 300,
              curve: Curve.Friction,
              onFinish: () => {
                this.sheetFlag = SheetState.BOTTOM;
              }
            }, () => {
              this.sheetHeight = 100;
            });
          }
          // 上面onActionUpdate中半模态高度为最低或最高时,也能进入上、下滑判断,会导致高度为99或791,所以在onActionEnd中重新赋值,保障最高最低高度;
          if (this.sheetHeight < 100) {
            this.sheetHeight = 100;
            this.sheetFlag = SheetState.BOTTOM;
          } else if (this.sheetHeight > 790) {
            this.sheetHeight = 790;
            this.sheetFlag = SheetState.TOP;
          }
          // 记录这次滑动结束后的值,也是下次滑动开始的值
          this.positionY = this.sheetHeight;
          console.info('Pan end');
        })
    )
    .width('100%')
    .height('100%');
  }

  watchFlag() {
    console.info(`watchFlag is: ${this.sheetFlag}`);
  }

  watchHeight() {
    console.info(`watchHeight is: ${this.sheetHeight}`);
  }

  build() {
    Column() {
      Button('transition modal 1')
        .onClick(() => {
          this.isShow = true;
        })
        .fontSize(20)
        .margin(10)
        .bindSheet($$this.isShow, this.myBuilder(), {
          height: this.sheetHeight,
          // backgroundColor: Color.Green,
          showClose: false,
          onWillDismiss: ((DismissSheetAction: DismissSheetAction) => {
            if (DismissSheetAction.reason == DismissReason.SLIDE_DOWN) {
              //在最低档位下拉关闭时,拦截下拉关闭事件,不做处理=>业务上不用关闭半模态页面
            }
          }),
        });
    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%');
  }
}

// 半模态状态:高中低
enum SheetState {
  TOP,
  MID,
  BOTTOM,
}

更多关于HarmonyOS鸿蒙Next中半模态(bindSheet)在最低档如何禁止下滑?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,要禁止半模态(bindSheet)在最低档下滑,可通过设置dragBar属性为false来实现。这将隐藏拖拽条并禁用下滑手势。

在HarmonyOS Next中,要实现在半模态(bindSheet)最低档位时禁止下滑,可以通过监听onWillDismiss事件并配合状态控制来实现。具体方法如下:

  1. 在Sheet组件的onWillDismiss回调中判断当前是否处于最小档位,如果是则阻止关闭操作。
  2. 使用状态变量控制Sheet的可滑动行为,在最小档位时禁用滑动。

示例代码:

@State isMinPosition: boolean = false

build() {
  Sheet(this.presenter)
    .onWillDismiss(() => {
      if (this.isMinPosition) {
        // 阻止关闭
        return false
      }
      return true
    })
    // ...其他配置
}

同时,需要在Sheet的onHeightChange回调中更新isMinPosition状态,通过判断当前高度是否等于最小高度来确定是否处于最小档位。

这种方式可以模拟花瓣地图的效果,在最小档位时阻止用户继续下滑,避免出现回弹现象。注意需要合理设置Sheet的最小高度和档位配置,确保交互逻辑符合预期。

回到顶部