实现文件List拖动鸿蒙示例代码

本文原创发布在华为开发者社区

介绍

本示例基于显式动画、List组件实现了文件推动、插入效果。

实现文件List拖动源码链接

效果预览

使用说明

运行项目前,请执行 ohpm install @ohos/hamock,下载hamock依赖

实现思路

  1. List手势拖动:
typescript 复制代码
// 以下组合手势为顺序识别,当长按手势事件未正常触发时,则不会出发拖动手势事件
GestureGroup(GestureMode.Sequence,
  // 长按
  LongPressGesture()
    .onAction((event: GestureEvent) => {
      this.currentData = item;
      this.isLongPress = true;
      this.listExchangeCtrl.onLongPress(item);
      this.listExchangeCtrl.originListOffsetY = this.listScroller.currentOffset().yOffset;
    }),
  // 拖动
  PanGesture()
    .onActionStart(() => {
    // 计算list开始拖动的偏移
      this.listExchangeCtrl.originListOffsetY = this.listScroller.currentOffset().yOffset;
    })
    .onActionUpdate((event: GestureEvent) => {
      // 计算item移动坐标时,需要算上list滚动的距离
      this.listExchangeCtrl.onMove(item, event.offsetY, this.listScroller.currentOffset().yOffset);
      let curListOffset = this.listScroller.currentOffset()
      // 获取手指信息
      let fingerInfo = event.fingerList[0]
      let clickPercentY =
        (fingerInfo.globalY - Number(this.listArea.globalPosition.y)) / Number(this.listArea.height)
      if (clickPercentY > 0.8 && !this.listScroller.isAtEnd()) {
        let scrollVelocity = clickPercentY > 0.9 ? 4 : 2
        if (this.listMaxScrollOffsetY - curListOffset.yOffset > scrollVelocity + 5) {
          this.listScroller.scrollTo({xOffset: 0, yOffset: curListOffset.yOffset += scrollVelocity})
        }
      } else if (clickPercentY < 0.2 && curListOffset.yOffset >= 0) {
        let scrollVelocity = clickPercentY < 0.1 ? 4 : 2
        if (curListOffset.yOffset > scrollVelocity + 5) {
          this.listScroller.scrollTo({xOffset: 0, yOffset: curListOffset.yOffset -= scrollVelocity})
        }
      }
    })
    .onActionEnd((event: GestureEvent) => {
      this.listExchangeCtrl.onDrop(item);
      this.listExchangeCtrl.originListOffsetY = this.listScroller.currentOffset().yOffset;
    })
).onCancel(() => {
  if (!this.isLongPress) {
    return;
  }
  this.listExchangeCtrl.onDrop(item);
})
  1. List Item拖动、放置、改变位置的处理(具体参考ListExchangeCtrl):
typescript 复制代码
onMove(item: T, offsetY: number): void {
    const index: number = this.deductionData.indexOf(item);
    this.offsetY = offsetY - this.dragRefOffset + scrollerOffset - this.originListOffsetY;
    this.modifier[index].offsetY = this.offsetY;
    const direction: number = this.offsetY > 0 ? 1 : -1;
    // 触发拖动时,被覆盖子组件缩小与恢复的动画
    const curveValue: ICurve = curves.initCurve(Curve.Sharp);
    const value: number = curveValue.interpolate(Math.abs(this.offsetY) / ITEM_HEIGHT);
    const shrinkScale: number = 1 - value / 10; // 计算缩放比例,value值缩小10倍
    if (index < this.modifier.length - 1) { // 当拖拽的时候,被交换的对象会缩放
      this.modifier[index + 1].scale = direction > 0 ? shrinkScale : 1;
    }
    if (index > 0) {
      this.modifier[index - 1].scale = direction > 0 ? 1 : shrinkScale;
    }
    //处理列表项的切换操作
    if (Math.abs(this.offsetY) > ITEM_HEIGHT / 2) {
      animateTo({ curve: Curve.Friction, duration: ANIMATE_DURATION }, () => {
        this.offsetY -= direction * ITEM_HEIGHT;
        this.dragRefOffset += direction * ITEM_HEIGHT;
        this.modifier[index].offsetY = this.offsetY;
        const target = index + direction // 目标位置索引
        if (target !== -1 && target <= this.modifier.length) {
          this.changeItem(index, target);
        }
      })
    }
 }
typescript 复制代码
/**
 * ListItem放置函数
 * @param item
 */
onDrop(item: T): void {
  const index: number = this.deductionData.indexOf(item);
  this.dragRefOffset = 0;
  this.offsetY = 0;
  AppStorage.setOrCreate('isLongPress', false);
  /**
   * 恢复拖动中,被缩小的子组件,并提供动画。
   * 通过interpolatingSpring(0, 1, 400, 38)构造插值器弹簧曲线对象初始速度为0,质量为1,刚度为400,阻尼为38
   */
  animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
    this.state = OperationStatus.DROPPING;
    if (index < this.modifier.length - 1) {
      this.modifier[index + 1].scale = 1;
    }
    if (index > 0) {
      this.modifier[index - 1].scale = 1;
    }
  })
  /**
   * 恢复被拖拽子组件的放大与阴影效果,并提供动画。
   * 通过interpolatingSpring(0, 1, 400, 38)构造插值器弹簧曲线对象初始速度为14,质量为1,刚度为170,阻尼为17
   */
  animateTo({ curve: curves.interpolatingSpring(14, 1, 170, 17) }, () => {
    this.state = OperationStatus.IDLE;
    this.modifier[index].hasShadow = false;
    this.modifier[index].scale = 1; // 初始化缩放比例
    this.modifier[index].offsetY = 0; // 初始化偏移量
  })
 }
相关推荐
于慨21 小时前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
石小石Orz21 小时前
油猴脚本实现生产环境加载本地qiankun子应用
前端·架构
从前慢丶21 小时前
前端交互规范(Web 端)
前端
CHU72903521 小时前
便捷约玩,沉浸推理:线上剧本杀APP功能版块设计详解
前端·小程序
GISer_Jing21 小时前
Page-agent MCP结构
前端·人工智能
王霸天21 小时前
💥别再抄网上的Scale缩放代码了!50行源码教你写一个永不翻车的大屏适配
前端·vue.js·数据可视化
小领航21 小时前
用 Three.js + Vue 3 打造炫酷的 3D 行政地图可视化组件
前端·github
@大迁世界21 小时前
2026年React大洗牌:React Hooks 将迎来重大升级
前端·javascript·react.js·前端框架·ecmascript
PieroPc21 小时前
一个功能强大的 Web 端标签设计和打印工具,支持服务器端直接打印到局域网打印机。Fastapi + html
前端·html·fastapi
悟空瞎说1 天前
深入 Vue3 响应式:为什么有的要加.value,有的不用?从设计到源码彻底讲透
前端·vue.js