如何通过拖拽拉起半模态弹窗
=============

#### 问题现象

如何通过拖拽方式拉起半模态弹窗？例如通过上拉将半模态弹窗引出。

#### 效果预览

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/d1/v3/LmkBtuUjT8er2owxDL9k7Q/zh-cn_image_0000002658958967.png?HW-CC-KV=V1&HW-CC-Date=20260709T133944Z&HW-CC-Expire=86400&HW-CC-Sign=515F38695A4091781F3040A12BC49D2C02519C1D1F3875A88928379DFFC9E8EC "点击放大")

#### 背景知识

*   [半模态转场](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-sheet-transition)：通过[bindSheet](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-sheet-transition#bindsheet)属性为组件绑定半模态页面，在组件插入时可通过设置自定义或默认的内置高度确定半模态大小，点击后显示模态页面。
*   在[绑定手势方法](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-gesture-settings)中，[gesture](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-gesture-settings#gesture)属性能够给组件绑定手势，手势识别成功后可以通过事件回调通知组件。还可以通过[组合手势](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-combined-gestures)的方法将多种手势组合为复合手势，支持顺序识别、并发识别和互斥识别。

#### 解决方案

通过[draggable](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-drag-drop#draggable)将文本组件设置为可拖动的。使用bindSheet将文本组件与一个半模态窗口Sheet绑定，并且使用gesture为文本组件添加手势监听，当用户拖动文本组件时，触发手势的更新事件，将bindSheet的属性isShow设置为true，从而显示半模态弹窗，实现拖拽拉起半模态弹窗。

@Entry
@Component
struct SheetTransitionExample {
  @State numbers: number\[\] \= \[\];
  @State isShow: boolean \= false;
  @State sheetHeight: number \= 300;

  aboutToAppear() {
    for (let i \= 1; i <\= 111; i++) {
      this.numbers.push(i);
    }
  }

  _// 使用@Builder装饰器定义一个可复用的UI构建器myBuilder。_
  @Builder
  myBuilder() {
    Column() {
      List() {
       _ // 使用ForEach遍历numbers数组生成列表项。_
        ForEach(this.numbers, (item: string) \=\> {
          ListItem() {
            Text(item.toString())
              .fontSize(16)
              .textAlign(TextAlign.Center)
              .size({ height: 100, width: '100%' });
          }.margin(10)
          .borderRadius(10)
          .backgroundColor('#F1F3F5')
        }, (item: string) \=\> item)
          .onMove((from: number, to: number) \=\> {
            let tmp \= this.numbers.splice(from, 1);
            this.numbers.splice(to, 0, tmp\[0\]);
          });
      }
      .lanes({ minLength: 80, maxLength: 400 })
      .width('100%')
      .height('100%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.White)
    .padding({ top: 5 });
  }

  build() {
    Column() {
      Text('向上拖')
        .draggable(true) _// 允许该文本被拖动。_
        .fontSize(20)
        .margin(10)
         _ // 将文本与Sheet绑定，点击或拖动可触发显示myBuilder内容。_
        .bindSheet($$this.isShow, this.myBuilder(), {
          height: this.sheetHeight,
          backgroundColor: '#F1F3F5',
          blurStyle: BlurStyle.Thick,
          showClose: true,
          title: { title: 'title', subtitle: 'subtitle' },
        })
        _  // 添加手势识别器，监听拖动手势。_
        .gesture(
          GestureGroup(GestureMode.Sequence, _// 设置手势模式为顺序执行。_
            PanGesture({ fingers: 1, direction: null, distance: 0 })
              .onActionUpdate((event: GestureEvent) \=\> {
                this.isShow \= true;
                console.info('输出：', event.offsetY);
                this.sheetHeight \= Math.abs(event.offsetY);
              })
          )
        );
    }
    .justifyContent(FlexAlign.End)
    .width('100%')
    .height('100%');
  }
}

#### 总结

本文通过给可拖动组件绑定组合手势来控制半模态弹窗的显隐，实现通过拖拽拉起半模态弹窗的效果。