【HarmonyOS NEXT】滑动选中放大卡片效果 在 HarmonyOS NEXT 中实现滑动选中居中放大的卡片效果是一种能显著提升内容浏览体验的经典交互模式。这种设计将用户的视觉焦点自然引导至屏幕中央的卡片常用于图文展示、视频推荐或产品陈列等场景。下面是实现这一效果的核心方案、关键代码和实战要点。核心方案Swiperscale属性动画实现该效果最直接、高效的方式是使用Swiper组件并配合scale属性来控制卡片的缩放。其核心思路是监听Swiper的滑动事件根据当前页索引与每一张卡片索引的距离动态计算并更新卡片的缩放比例。整体交互流程如下图所示关键代码实现以下是基于上述方案的核心代码实现你可以根据项目需求进行调整。1. 准备卡片数据首先我们准备一些演示用的卡片数据。为了动态控制每个卡片的缩放我们需要一个状态数组来管理它们各自的缩放值。typescript// 1. 定义卡片数据模型 class CardItem { id: number; content: string; // 用于控制卡片缩放的状态变量 State scale: number 1.0; constructor(id: number, content: string) { this.id id; this.content content; } } Entry Component struct SwiperCardPage { // 初始化卡片数据 State cardList: CardItem[] [ new CardItem(1, 卡片 1), new CardItem(2, 卡片 2), new CardItem(3, 卡片 3), new CardItem(4, 卡片 4), new CardItem(5, 卡片 5), ]; // 记录当前居中卡片的索引 State currentIndex: number 0; // ...后续代码 }2. 计算卡片缩放逻辑接下来我们需要一个方法它根据当前居中的卡片索引计算列表中每一张卡片应有的缩放值。通常距离中心越远的卡片缩放值越小。typescript// 计算卡片缩放比例 private calculateScale(index: number): number { // 计算当前卡片与居中卡片的索引距离 const distance Math.abs(index - this.currentIndex); // 基础缩放值为0.8距离每增加1缩放值减小0.1最大为1.2 // 这样居中卡片 (distance0) 缩放为 1.2两侧卡片依次减小 const baseScale 0.8; const scaleStep 0.1; // 确保缩放值不小于最小限制居中卡片保持最大缩放 if (distance 0) { return 1.2; } else { return Math.max(baseScale, 1.2 - distance * scaleStep); } } // 在滑动事件中更新所有卡片的缩放状态 private updateCardsScale(index: number) { this.currentIndex index; // 遍历列表为每个卡片对象更新scale属性 this.cardList.forEach((item, i) { item.scale this.calculateScale(i); }); }3. 构建UI与绑定动画最后在build()方法中我们使用Swiper组件来呈现卡片列表并监听onChange事件来触发缩放更新。给卡片添加.animation()修饰器能确保缩放变化时过渡平滑。typescriptbuild() { Column() { Swiper() { ForEach(this.cardList, (item: CardItem, index: number) { Column() { Text(item.content) .fontSize(24) .fontColor(Color.White) } .width(90%) .height(200) .backgroundColor(#FF6B6B) .borderRadius(16) .shadow({ radius: 12, color: #44000000 }) // 关键点1: 绑定缩放状态 .scale({ x: item.scale, y: item.scale }) // 关键点2: 添加属性动画使缩放变化平滑 .animation({ duration: 300, curve: Curve.EaseOut, }) .margin({ left: 10, right: 10 }) }, (item: CardItem) item.id.toString()) } // 关键点3: 监听滑动事件更新缩放 .onChange((index: number) { this.updateCardsScale(index); }) .width(100%) .height(260) // 开启循环播放提升体验 .loop(true) // 显示导航点指示器 .indicator(true) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) }精进实战更多优化技巧掌握了基础实现后你还可以通过以下技巧让你的卡片效果更上一层楼更生动的弹性动画animateTo配合interpolatingSpring曲线可以创建更具物理感的弹性动画让卡片的缩放变化更像是在呼吸。typescript// 在更新缩放时使用 animateTo({ duration: 400, curve: curves.interpolatingSpring(0, 1, 400, 40), // 弹簧曲线 }, () { this.updateCardsScale(index); });透明度与缩放协同为了进一步强化焦点可以让两侧的卡片不仅缩小透明度也略微降低使居中的卡片更加突出。typescript// 在calculateScale方法中同时返回缩放和透明度 // 并绑定到 CardItem 的 .opacity() 属性上 private calculateScaleAndOpacity(index: number) { // ... const opacity Math.max(0.6, 1 - distance * 0.1); return { scale: ..., opacity: opacity }; }性能考量如果卡片数量非常庞大例如上百张频繁更新所有卡片的State可能会带来性能开销。此时可以考虑组件化方案将卡片抽离为独立组件仅更新可见区域的卡片状态或者利用LazyForEach进行懒加载以优化长列表性能。常见问题与避坑指南animateTo不生效animateTo是显式动画必须在事件回调函数如onClick中调用。直接放在build()函数或aboutToAppear()生命周期里是不会生效的。Swiper与Scroll如何选对于标准的滑动翻页居中显示效果Swiper是首选。它内置了分页、手势和指示器实现成本最低。Scroll组件更适合需要自定义滚动行为或实现类似联动滚动等复杂场景的情况。