如何实现组件弹簧拉伸回缩动效
==============

#### 问题现象

如何实现组件弹簧动效？即滑动时不是边缘回弹，而是组件向滑动方向放大，滑动结束后回缩。

#### 效果预览

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/53/v3/Xh51fsFaSJGVwZiVmMChcg/zh-cn_image_0000002658795063.png?HW-CC-KV=V1&HW-CC-Date=20260709T134046Z&HW-CC-Expire=86400&HW-CC-Sign=05AA32B84A4DE46698EE1AE36EF608DFF166DF83D0F28C8DB99D5856D166642E "点击放大")

#### 背景知识

*   [PanGesture](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-gesture-events-single-gesture#滑动手势pangesture)：滑动手势用于触发滑动手势事件，滑动达到最小滑动距离（默认值为5vp）时滑动手势识别成功。通过滑动手势的实时回调更新组件状态达成弹簧动效。
*   [scale](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-transformation#scale18)：设置组件缩放。通过控制组件的缩放中心点以及缩放大小实现动效。
*   [animateTo](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-explicit-animation)：提供全局animateTo显式动画接口来指定由于闭包代码导致的状态变化插入过渡动效。

#### 解决方案

1.  给组件绑定滑动手势，设置移动和手指抬起时的回调。
2.  滑动过程中使用百分比的形式保证缩放连续性以及边界效果。
3.  滑动结束，使用[onActionEnd](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-gestures-pangesture#onactionend)设置滑动手势结束回调，将缩放比例设置回原大小。

示例代码参考如下：

@Entry
@Component
struct SpringAnimation {
  data: Array<number> = \[0, 1, 2, 3, 4, 5\];
  @State scaleNumber: number = 1;

  build() {
    Column({ space: 10 }) {
      _// 构建内容组件_
      ForEach(this.data, (index: number) => {
        Column() {
          Text('欢迎使用HarmonyOS-' + index);
        }
        .height('10%')
        .width('90%')
        .borderRadius(12)
        .backgroundColor('#f1f3f5')
        .alignItems(HorizontalAlign.Center)
        .justifyContent(FlexAlign.Center)
        .scale({ y: 1 / this.scaleNumber, centerY: 0 });
      });
    }
    .height('100%')
    .width('100%')
    .scale({ y: this.scaleNumber, centerY: 0 }) _// 设置纵向拉伸以及拉伸中心点_
    .gesture(
      PanGesture()
        .onActionUpdate((event) => {
          _// 通过滑动距离设置拉伸倍率_
          if (event.offsetX > 0 && event.offsetX < 100) {
            _// 向下滑动且控制最大响应滑动距离100和最大伸缩倍率1.5_
            this.scaleNumber = event.offsetX \* 0.005 + 1;
          }
        })
        .onActionEnd(() => {
          _// 拉伸结束后返回原大小_
          this.getUIContext()?.animateTo({
            duration: 200,
            playMode: PlayMode.Normal,
          }, () => {
            this.scaleNumber = 1;
          });
        })
    );
  }
}