【HarmonyOS 5.0.0 或以上】构建滑动吸附组件 SwipeSnapView:滑动后自动对齐吸附 / 可设置吸附点 / 滑动阻尼

🎯 目标

实现一个带有“吸附感”的滑动容器组件 SwipeSnapView,适用于:

  • 滑动卡片、分页、滚动面板等场景

  • 用户滑动后自动吸附到最近的卡片或位置

  • 可设置吸附点位置、间距、自定义回弹动画

  • 支持横向/纵向布局

  • 后续可拓展为分页器、滑动菜单、多列布局吸附等


🧱 动效示意结构

⬅️ 拖动卡片 → 卡片自动对齐吸附 ✅
| [卡片1] [卡片2] [卡片3] |
   ← 自动吸附对齐卡片边界

🧰 组件实现:SwipeSnapView.ets(横向滑动 + 自动吸附)

@Component
export struct SwipeSnapView {
  @Prop items: string[] = []
  @Prop itemWidth: number = 120
  @Prop gap: number = 12
  @Prop onSnap: (index: number) => void = () => {}

  @State offset: number = 0

  build() {
    let startX = 0
    let lastX = 0
    let totalWidth = this.items.length * (this.itemWidth + this.gap)

    Row()
      .width('100%')
      .height(140)
      .clip(true)
      .onTouch(event => {
        if (event.type === TouchType.Down) {
          startX = event.touches[0].x
          lastX = this.offset
        } else if (event.type === TouchType.Move) {
          const delta = event.touches[0].x - startX
          this.offset = Math.min(0, Math.max(-(totalWidth - 360), lastX + delta))
        } else if (event.type === TouchType.Up || event.type === TouchType.Cancel) {
          this.snapToClosest()
        }
      }) {
      Row()
        .translate({ x: this.offset })
        .animateTo({ duration: 300, curve: Curve.EaseOut }) {
        this.items.forEach(item => {
          Column()
            .width(this.itemWidth)
            .height(120)
            .margin({ right: this.gap })
            .backgroundColor('#F1F9FF')
            .borderRadius(12)
            .alignItems(HorizontalAlign.Center)
            .justifyContent(FlexAlign.Center) {
            Text(item).fontSize(14).fontColor('#007DFF')
          }
        })
      }
    }
  }

  private snapToClosest() {
    const rawIndex = Math.round(-this.offset / (this.itemWidth + this.gap))
    const index = Math.max(0, Math.min(this.items.length - 1, rawIndex))
    this.offset = -index * (this.itemWidth + this.gap)
    this.onSnap(index)
  }
}

📦 使用示例

@Entry
@Component
struct DemoSwipeSnap {
  @State current: number = 0

  build() {
    Column({ space: 16 }).padding(20) {
      Text(`当前滑动至第 ${this.current + 1} 个卡片`).fontSize(14).fontColor('#333')

      SwipeSnapView({
        items: ['卡片1', '卡片2', '卡片3', '卡片4', '卡片5'],
        onSnap: index => this.current = index
      })
    }
  }
}

✨ 可扩展能力建议

功能说明
横/纵双向支持设置方向参数支持 vertical 模式
吸附位置可自定义允许配置吸附到“左对齐”“中间对齐”等
支持分页模式 + 指示器加入分页圆点显示当前页卡片
滑动速度/距离智能判断吸附目标快速滑动吸附下一个,慢滑吸附最近

📘 下一篇预告

第6篇:【HarmonyOS 5.0.0 或以上】构建弹性回弹容器 ElasticBounceBox:拖动松手回弹 / 支持阻尼 / 过拖吸边动画

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值