HarmonyOS 6.0 拖拽排序与列表动画
列表排序看起来简单——不就是数组元素换个位置吗但加上动画和交互就有很多细节拖拽时视觉反馈、松手后的位置动画、排序过程中的状态同步。这篇从手动排序到拖拽排序把动画细节全说清楚。数组重排算法最基础的操作——把数组元素从位置A移到位置BprivatemoveItem(from:number,to:number):void{letitem:DataItemthis.items[from];letnewItems:DataItem[][];// 先移除from位置的元素for(leti:number0;ithis.items.length;i){if(i!from){newItems.push(this.items[i]);}}// 在to位置插入letresult:DataItem[][];for(leti:number0;inewItems.length;i){if(ito){result.push(item);}result.push(newItems[i]);}if(tonewItems.length){result.push(item);}this.itemsresult;}关键必须给this.items赋值新数组不能直接splice修改。State数组的变更检测依赖引用变化直接修改原数组不会触发UI刷新。更简洁的方式如果运行时支持letitem:DataItemthis.items.splice(from,1)[0];this.items.splice(to,0,item);this.items[...this.items];// 触发刷新但ArkTS不支持解构和展开运算符所以用for循环构建新数组是最安全的做法。上下移动按钮最简单的排序UI——每个列表项旁边有↑↓按钮Row(){Text(item.title).layoutWeight(1)Button(↑).onClick((){if(index0){this.moveItem(index,index-1);}})Button(↓).onClick((){if(indexthis.items.length-1){this.moveItem(index,index2);// 2因为移除后索引偏移}})}moveItem的to参数要注意移除from元素后目标位置的含义会变。上移一位是index-1下移一位是index2因为当前元素被移除后后面的元素都前移了一位。位置动画排序后列表项位置变化加animation让变化有过渡ListItem(){Row(){// 内容}.width(100%).padding(12).borderRadius(10).backgroundColor(#ffffff).animation({duration:200,curve:Curve.EaseInOut})}animation修饰器加在ListItem的内容容器上。当items数组重排后ForEach根据key重新分配数据组件位置变化animation让位移产生过渡动画。ForEach的key必须稳定——用item.id不要用index。index在排序后会变导致ForEach错误地复用组件动画就会错乱。拖拽项视觉反馈拖拽中的列表项需要视觉区分——背景色变化、阴影加深、轻微放大StatedragIndex:number-1;Row(){// 内容}.backgroundColor(this.dragIndexindex?#e3f2fd:#ffffff).shadow({radius:this.dragIndexindex?8:2,color:#1a000000,offsetY:this.dragIndexindex?2:1}).scale({x:this.dragIndexindex?1.02:1,y:this.dragIndexindex?1.02:1}).animation({duration:150,curve:Curve.EaseOut})三个视觉变化同时发生背景变蓝、阴影加深、轻微放大1.02倍。animation让变化有过渡。dragIndex在拖拽开始时设为当前index拖拽结束时恢复-1。PanGesture拖拽排序用PanGesture实现跟手拖拽Row(){Text(≡)// 拖拽手柄Text(item.title)}.gesture(PanGesture().onActionStart((){this.dragIndexindex;}).onActionUpdate((event:GestureEvent){// 根据event.offsetY计算当前应该在的位置letcurrentY:numberthis.itemPositions[index]event.offsetY;letnewIndex:numberthis.calculateNewIndex(currentY);if(newIndex!indexnewIndex!this.targetIndex){this.targetIndexnewIndex;}}).onActionEnd((){if(this.targetIndex0){this.moveItem(this.dragIndex,this.targetIndex);}this.dragIndex-1;this.targetIndex-1;}))PanGesture的onActionUpdate提供offsetY——手指从起始位置的偏移。根据偏移计算当前应该在哪个位置实时预览排序效果。但PanGesture有个问题ListItem的PanGesture会跟List的滚动冲突。需要用priorityGesture或者用专门的拖拽手柄区域。List内置拖拽APIList组件有onDragStart/onDrop等拖拽回调List(){ForEach(this.items,(item:DataItem,index:number){ListItem(){Row(){Text(item.title)}}.onDragStart((event:DragEvent){this.dragIndexindex;returnthis.dragBuilder(item);}).onDrop((event:DragEvent){lettargetIndex:numberthis.calculateDropIndex(event.y);this.moveItem(this.dragIndex,targetIndex);this.dragIndex-1;})},(item:DataItem)item.id)}onDragStart返回一个Builder作为拖拽预览图。onDrop在松手时触发event.y可以计算放置位置。但onDragStart/onDrop在List中的行为不够稳定——长按触发拖拽的灵敏度、跨ListItem的拖拽位置计算都有坑。实际项目中更常用按钮排序动画过渡而不是真正拖拽。删除动画列表项删除时的动画效果StateremovingIndex:number-1;Row(){Text(item.title)}.scale({x:this.removingIndexindex?0.8:1,y:this.removingIndexindex?0.8:1}).opacity(this.removingIndexindex?0:1).animation({duration:200,curve:Curve.EaseIn})// 触发删除removeItem(index:number):void{this.removingIndexindex;setTimeout((){letnewItems:DataItem[][];for(leti:number0;ithis.items.length;i){if(i!index){newItems.push(this.items[i]);}}this.itemsnewItems;this.removingIndex-1;},200);}删除分两步先动画缩小渐隐200ms后真正移除数据。setTimeout延迟匹配animation时长。插入动画新项目插入时渐显放大StatenewItemIndex:number-1;Row(){Text(item.title)}.scale({x:this.newItemIndexindex?1:0.5,y:this.newItemIndexindex?1:0.5}).opacity(this.newItemIndexindex?1:0).animation({duration:300,curve:Curve.EaseOut})// 插入后insertItem(item:DataItem,at:number):void{this.newItemIndexat;letnewItems:DataItem[][];for(leti:number0;ithis.items.length;i){if(iat){newItems.push(item);}newItems.push(this.items[i]);}if(atthis.items.length){newItems.push(item);}this.itemsnewItems;setTimeout((){this.newItemIndex-1;},300);}初始scale 0.5和opacity 0动画到1和1。300ms后清除标记。这样新插入的项有一个出现的动画。列表动画的性能注意动画越多性能开销越大。几个优化点只对可见项做动画removingIndex/newItemIndex只影响一个项animation时长别太长200ms够用超过300ms用户会觉得卡避免大范围重绘排序时ForEach只更新变化项key要稳定shadow开销大大量列表项加shadow会掉帧拖拽中临时加shadow结束后去掉LazyForEach cachedCount大列表排序必须用LazyForEach否则ForEach全量创建组件交错动画列表初始化时每项依次出现StateitemVisible:boolean[][];aboutToAppear():void{for(leti:number0;ithis.items.length;i){setTimeout((){this.itemVisible[i]true;},i*50);}}Row(){Text(item.title)}.opacity(this.itemVisible[index]?1:0).translate({y:this.itemVisible[index]?0:20}).animation({duration:300,curve:Curve.EaseOut})每项延迟50ms出现形成从上到下的波浪效果。50ms * N项 总延迟。10项500ms完成。踩坑清单问题原因解决排序后UI没更新直接修改数组没触发刷新赋值新数组给this.items动画错乱ForEach用index作key改用item.idPanGesture跟滚动冲突两个手势互相消费用priorityGesture或拖拽手柄删除动画后列表跳动画和数据移除不同步动画完成后再移除数据阴影导致掉帧大量shadow组件只在拖拽项加shadow移动位置计算错误移除元素后索引偏移先移除再计算目标位置交错动画闪烁opacity初始值不对初始化itemVisible为false按钮排序↑↓方向反moveItem的to参数理解错误下移用index2不是index1插入位置不准for循环的插入逻辑在iat时先push新项动画中操作列表动画未完成就改数据加loading标记防重复操作列表动画的原则是少量项做动画大量项保性能。排序只影响两三个项的位置变化用animation就够了。全列表出现动画只在初始化做一次不要反复触发。

相关新闻

CC Switch:一站式AI工具管理平台,提升开发者工作效率的5个实用技巧

CC Switch:一站式AI工具管理平台,提升开发者工作效率的5个实用技巧

CC Switch:一站式AI工具管理平台,提升开发者工作效率的5个实用技巧 【免费下载链接】cc-switch A cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Gemini CLI & Hermes Agent. Only official website: c…

2026/11/13 20:42:12 阅读更多 →
如何高效使用四叶草拼音:打造纯净无广告的专业中文输入体验 [特殊字符]️

如何高效使用四叶草拼音:打造纯净无广告的专业中文输入体验 [特殊字符]️

如何高效使用四叶草拼音:打造纯净无广告的专业中文输入体验 🍀️ 【免费下载链接】rime-cloverpinyin 🍀️四叶草拼音输入方案,做最好用的基于rime开源的简体拼音输入方案! 项目地址: https://gitcode.com/gh_mirror…

2026/11/12 2:16:04 阅读更多 →
Twitch视频下载终极指南:命令行工具快速保存直播内容

Twitch视频下载终极指南:命令行工具快速保存直播内容

Twitch视频下载终极指南:命令行工具快速保存直播内容 【免费下载链接】twitch-dl CLI tool for downloading videos from Twitch. 项目地址: https://gitcode.com/gh_mirrors/tw/twitch-dl 想要轻松下载Twitch平台的直播录像、精彩片段和聊天记录吗&#xff…

2026/11/1 23:00:30 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/13 5:13:25 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/13 5:13:25 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/13 5:13:25 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/12 18:22:26 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/12 18:22:25 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/13 5:13:25 阅读更多 →

日新闻

周新闻

月新闻