中国优质的IT技术网站
专业IT技术创作平台
IT职业在线教育平台
本文原创发布在华为开发者社区。
本示例基于显式动画、List组件实现了文件推动、插入效果。
实现文件List拖动源码链接
运行项目前,请执行 ohpm install @ohos/hamock,下载hamock依赖
// 以下组合手势为顺序识别,当长按手势事件未正常触发时,则不会出发拖动手势事件 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); })
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); } }) } }
/** * 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; // 初始化偏移量 }) }
微信扫码分享