HarmonyOS鸿蒙Next中如何实现中间放大两边缩小可滑动的轮播Banner效果

HarmonyOS鸿蒙Next中如何实现中间放大两边缩小可滑动的轮播Banner效果 轮播时,中间的放大效果,两边的缩放并且透明度减少。

3 回复

实现思路:

核心是使用 customContentTransition(transition: SwiperContentAnimatedTransition) 自定义Swiper页面切换动画。在页面跟手滑动和离手后执行切换动画的过程中,会对视窗内所有页面逐帧触发回调, 我们可以在回调中设置透明度、缩放比例、位移等属性来自定义切换动画。

切换过程中,切换到的页面缩放大小和透明度都为1。切出和未切入的根据切换距离计算。

预览效果:

cke_14709.gif

完整demo代码:

/**
 * @fileName : SwiperAnimation.ets
 * @author : @cxy
 * @date : 2025/12/19
 * @description : 文件描述
 */


@Component
export struct SwiperAnimation {
  @State list: string[] = [
    'https://picsum.photos/300/200?random=1',
    'https://picsum.photos/300/200?random=2',
    'https://picsum.photos/300/200?random=3',
    'https://picsum.photos/300/200?random=4',
    'https://picsum.photos/300/200?random=5']
  @State scaleList: number[] = [1];
  @State opacityList: number[] = [1];
  private indicatorController: IndicatorComponentController = new IndicatorComponentController();

  build() {
    Column() {
      Swiper() {
        ForEach(this.list, (item: string, index: number) => {
          Image(item)
            .width("calc(100%-120vp)")
            .height(200)
            .borderRadius(10)
            .scale({ x: this.scaleList[index] || 0.87, y: this.scaleList[index] || 0.87 })
            .opacity(this.opacityList[index] || 1)
        }, (item: string, index: number) => item + index)
      }
      .prevMargin(45)
      .nextMargin(45)
      .loop(true)
      .autoPlay(true)
      .interval(5000)
      .indicator(this.indicatorController)
      .indicatorInteractive(true)
      .width('100%')
      .onChange((index) => {
      })
      .customContentTransition({
        // 页面移除视窗时超时1000ms下渲染树
        timeout: 1000,
        // 对视窗内所有页面逐帧回调transition,在回调中修改opacity、scale、translate、zIndex等属性值,实现自定义动画
        transition: (proxy: SwiperContentTransitionProxy) => {
          const scale = 0.87
          const scaleFactor = 1 - scale * Math.abs(proxy.position); // 最小缩放为 scale
          this.scaleList[proxy.index] = Math.max(scaleFactor, scale);

          const opacity = 0.5
          const opacityFactor = 1 - opacity * Math.abs(proxy.position); // 最小透明度为 opacity
          this.opacityList[proxy.index] = Math.max(opacityFactor, opacity);
        }
      })

      IndicatorComponent(this.indicatorController)
        .initialIndex(1)
        .width('100%')
        .style(
          new DotIndicator()
            .itemWidth(6)
            .itemHeight(6)
            .selectedItemWidth(15)
            .selectedItemHeight(6)
            .color('#fff')
            .selectedColor('#ff0090ff')
        )
        .loop(true)
        .count(this.list.length)
        .onChange((index: number) => {
        })
    }
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Start)
    .width('100%')
    .backgroundColor('#aaa')
    .padding({
      top: 20
    })
  }
}

更多关于HarmonyOS鸿蒙Next中如何实现中间放大两边缩小可滑动的轮播Banner效果的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,可通过ArkUI的Swiper组件结合自定义插值器实现该效果。设置Swiper的loop为true开启循环,通过displayMode设置显示模式。关键是在Swiper的item组件上应用scale属性,使用自定义插值器根据滑动位置动态计算缩放比例,中间项为1,两侧按需缩小。同时调整item的zIndex确保层级正确。

在HarmonyOS Next中实现中间放大、两边缩小且可滑动的轮播Banner效果,可以通过ArkUI的Swiper组件结合自定义动画和布局属性来实现。以下是核心实现思路和关键代码示例:

1. 使用Swiper组件作为基础容器

Swiper组件支持水平滑动和分页显示,通过设置loop属性可实现循环轮播。

Swiper() {
  // 轮播项内容
}
.loop(true) // 循环播放
.autoPlay(true) // 自动播放
.indicator(false) // 隐藏默认指示器

2. 实现中间放大和两边缩小效果

通过SwiperonChange事件监听当前页索引,动态计算每个页面的缩放比例和透明度:

  • 中间页面:缩放比例为1.0(或更大),透明度为1.0
  • 两侧页面:缩放比例减小(如0.85),透明度降低(如0.6)
@State currentIndex: number = 0

Swiper() {
  ForEach(this.bannerList, (item: BannerItem, index: number) => {
    this.buildBannerItem(item, index)
  })
}
.onChange((index: number) => {
  this.currentIndex = index
})

3. 自定义轮播项样式

buildBannerItem方法中,根据当前索引与currentIndex的差值,动态设置每个项的缩放和透明度:

buildBannerItem(item: BannerItem, index: number) {
  // 计算当前项与中间项的偏移差
  let offset = Math.abs(index - this.currentIndex)
  let scale = offset === 0 ? 1.0 : 0.85 // 中间项放大,两侧缩小
  let opacity = offset === 0 ? 1.0 : 0.6 // 中间项不透明,两侧半透明

  Stack() {
    // 轮播内容
    Image(item.imageUrl)
      .width('100%')
      .height(200)
  }
  .scale({ x: scale, y: scale })
  .opacity(opacity)
  .animation({ duration: 200, curve: Curve.EaseInOut }) // 添加平滑动画
}

4. 调整布局和间距

通过marginpadding属性调整两侧页面的间距,确保中间项突出显示:

.margin({ left: offset === 1 ? 10 : 0, right: offset === 1 ? 10 : 0 })

5. 优化交互体验

  • 添加手势滑动事件,确保滑动流畅
  • 结合PageSlider或自定义指示器增强交互反馈
  • 使用displayPriority优化性能,避免过多页面同时渲染

注意事项

  • 确保SwiperindexcurrentIndex同步更新
  • 动画曲线建议使用Curve.EaseInOut保证平滑过渡
  • 可结合@Prop@Link实现组件间状态同步

此方案通过动态样式和动画实现了视觉差异,同时保持了滑动的流畅性。实际开发中可根据需求调整缩放比例、透明度和动画参数。

回到顶部