HarmonyOS7 显式动画与手势:拖拽回弹效果实现

文章目录

前言

"拖一下松手,东西自己弹回原位"------这种交互在你每天用的 app 里到处都是:下拉刷新、卡片左滑删除、悬浮球归边。

它其实是"手势 + 动画"的组合拳。这篇文章我带你从零写一个可拖拽、松手回弹 的小方块,把 PanGestureanimateTo 怎么配合讲透。代码不多,但套路是通用的。

先认识 PanGesture

PanGesture 是"拖拽手势",监听手指在屏幕上滑动。它有几个回调:开始拖、拖的过程中、松手。

代码实现

typescript 复制代码
@Local offsetX: number = 0
@Local offsetY: number = 0

build() {
  Stack() {
    Circle({ width: 60, height: 60 })
      .fill('#4A90E2')
      .offset({ x: this.offsetX, y: this.offsetY })   // 用 offset 控制位置
      .gesture(
        PanGesture()
          .onActionStart(() => { /* 手指按下开始拖 */ })
          .onActionUpdate((e) => {
            this.offsetX = e.offsetX   // 实时跟随手指
            this.offsetY = e.offsetY
          })
          .onActionEnd(() => { this.backToOrigin() })
      )
  }
  .width('100%')
  .height('100%')
}

逐行拆解:

  • .offset({ x, y }):给组件一个偏移量,正数向右/下。我们用它来"移动"方块,而不是改布局。
  • PanGesture().onActionUpdate((e) => ...)e.offsetX 是相对拖动起点的位移。把它赋给 offsetX,方块就实时跟着手指走。
  • .onActionEnd(...):手指松开时触发,在这里让方块弹回。

关键点:拖动时不要包 animateTo,否则会有延迟感。只有松手回弹时才需要动画。这是新手最容易搞反的地方。

松手回弹:animateTo 上场

typescript 复制代码
backToOrigin() {
  animateTo({ duration: 300, curve: Curve.Spring }, () => {
    this.offsetX = 0
    this.offsetY = 0
  })
}

松手后,用弹簧曲线把 offsetX/Y 动画回 0,方块就"嗖"地弹回起点。弹簧曲线让回弹有轻微过冲,手感更真实。

进阶:拖到边缘就吸附

很多场景不是弹回,而是"拖过一半就飞走,没过一半弹回"。加个判断:

核心代码

typescript 复制代码
.onActionEnd(() => {
  if (this.offsetX > 150) {
    // 拖够了,飞出屏幕右侧
    animateTo({ duration: 250 }, () => { this.offsetX = 400 })
  } else {
    // 没拖够,弹回
    animateTo({ duration: 250, curve: Curve.Spring }, () => { this.offsetX = 0 })
  }
})

offsetX > 150 是你自己定的"阈值",代表"用户拖得够远了,算他确认操作"。这种"阈值 + 二选一动画"的模式,在滑动删除、底部弹窗 dismiss 里都用得到。

一个完整可跑的例子

typescript 复制代码
@Entry
@ComponentV2
struct DragDemo {
  @Local x: number = 0
  @Local y: number = 0

  build() {
    Stack() {
      Circle({ width: 80, height: 80 })
        .fill('#FF6B6B')
        .offset({ x: this.x, y: this.y })
        .gesture(
          PanGesture()
            .onActionUpdate((e: GestureEvent) => {
              this.x = e.offsetX
              this.y = e.offsetY
            })
            .onActionEnd(() => {
              animateTo({ duration: 400, curve: curves.springMotion(0.5, 0.9) }, () => {
                this.x = 0
                this.y = 0
              })
            })
        )
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }
}

跑起来:按住红圆拖动,松手它带弹性地归位。这就是所有"拖拽回弹"交互的最小骨架。

常见误区(小白必踩)

误区 说明
拖动时也包 animateTo 会导致跟手有延迟感。记住:拖动过程实时跟手(不动画),只有松手回弹才动画。
松手不回弹 onActionEnd 里要用 animateTo 把位置动画回原点或用弹簧曲线弹回。
用布局属性代替 offset 拖拽用 .offset() 控制偏移最顺,改 margin/position 反而麻烦。

下面这段代码可以直接复制到 DevEco Studio 里运行。建议你边读边敲,改一改文末「动手改一改」里的参数,亲眼看看效果。

完整可运行示例:拖拽回弹小球

显式动画配合手势,做一个「拖出去松手弹回」的跟手效果。

完整示例

typescript 复制代码
// PanGesture 跟手拖动 + animateTo 松手回弹
@Entry
@Component
struct DragBall {
  @State offsetX: number = 0
  @State offsetY: number = 0

  build() {
    Stack() {
      Circle({ width: 80, height: 80 })
        .fill('#007DFF')
        .translate({ x: this.offsetX, y: this.offsetY })
        .gesture(
          PanGesture()
            .onActionUpdate((e) => {     // 手指移动时实时跟手
              this.offsetX = e.offsetX
              this.offsetY = e.offsetY
            })
            .onActionEnd(() => {         // 松手:弹回原点
              animateTo({ duration: 400, curve: Curve.Spring }, () => {
                this.offsetX = 0
                this.offsetY = 0
              })
            })
        )
    }
    .width('100%').height('100%')
  }
}

你会看到什么:一个蓝色圆球。手指按住拖动,圆球实时跟随手指;松手后弹回中心位置,带弹簧回弹手感。

动手改一改

  • onActionEnd 里的目标改成 offsetX = e.offsetX(需要缓存),做成「拖到哪停哪」。
  • Curve.Spring 改成 Curve.Friction,对比回弹与摩擦停下的差异。
  • 给圆球加 .scale({ x: 1.1, y: 1.1 }),拖拽时放大,松手还原。

写在最后

手势 + 动画的套路就三步:手势里改位置(实时跟手)、松手时判断、用 animateTo 做过渡。记住"拖动不动画、回弹才动画",手感立刻专业。

建议你把阈值判断那段加上,体验一下"拖够飞走、没拖够弹回"------理解了这个,滑动删除你就能自己写出来了。

相关推荐
想你依然心痛1 天前
HarmonyOS ArkUI Swiper 轮播组件深度封装与工程化实践
swiper·arkui·lazyforeach·轮播组件·自动播放·指示器定制·一多适配
想你依然心痛1 天前
HarmonyOS ArkUI Scroll 滚动容器组件深度封装与工程化实践
arkui·下拉刷新·scroll·上拉加载·lazyforeach·滚动容器·嵌套滚动
想你依然心痛2 天前
Slider 滑块组件深度解析与交互定制全攻略
harmonyos·arkui·slider·双向绑定·sliderchange·sliderange·contentmodifier
风华圆舞9 天前
rawfile 资源与强类型词库加载器:schema / data / source 三层版本
harmonyos·arkui·resourcemanager·rawfile·arkts 编译·arkts 强类型
熊猫钓鱼>_>10 天前
ArkUI 动画实战:从理论到交互,构建流畅的鸿蒙动效体验
华为·架构·交互·harmonyos·arkts·arkui·native
2301_7681034910 天前
HarmonyOS趣味相机实战第31篇:图片文档List、详情弹层与删除状态闭环
list·harmonyos·arkts·状态管理·arkui
翼辉cto13 天前
网络请求与数据交互:http 模块、拦截器与状态封装
移动开发·harmonyos·arkts·鸿蒙·arkui
翼辉cto13 天前
鸿蒙生态与 ArkTS 入门:环境搭建与第一个应用
移动开发·harmonyos·arkts·鸿蒙·arkui
想你依然心痛14 天前
【共创季稿事节】HarmonyOS 6.1 沉浸光感助力音乐 App 氛围感设计案例
音乐播放器·arkui·沉浸光感·harmonyos 6.1·封面取色·动态主题·全屏沉浸
2301_7681034914 天前
HarmonyOS趣味相机实战第14篇:装饰素材强类型建模、锚点语义与套装组合校验
harmonyos·arkts·数据建模·arkui·相机贴纸