鸿蒙开发:自定义切换动画实现Swiper层叠滑动效果

前言

本文基于Api13

Swiper组件默认情况下是无论是从左往右,还是从右往左,都是平移滑动,效果如下:

以上的效果是最常见的,基本上都够满足大部分的场景需求,也是很多主流App首选效果,但是,仍然有一部分需求,会不按常理出牌,这不,让实现一个层叠滑动的效果就来了,所谓的层叠,就是,在滑动条目的时候,上一个不动,下一个覆盖到上一个的上面进行滑动,为了更好的便于大家理解,简单的做一个小动画,模拟演示一下。

正常的平移滑动效果,两个条目会一起进行平移。

层叠效果滑动,可以看到,上一个条目是不会跟随移动的。

那么如何实现上一个条目不随着手势进行移动呢?这里就需要用到自定义页面切换动画了,也就是在手势滑动的时候,保持当前条目不动,让下一个条目进行移动,还有一点需要注意的是,那就是需要设置组件的堆叠顺序,使其要滑动的组件层级高于前一个组件。

实现之前,我们先了解一下自定义Swiper页面切换动画customContentTransition。

 

customContentTransition

customContentTransition是Swiper组件中的一个属性,主要用于页面切换动画,接收一个SwiperContentAnimatedTransition参数;它的作用,主要在于,在页面随着手势进行切换动画的过程中,会对视窗内所有页面进行逐帧触发回调,在这个回调中,我们就可以针对条目进行设置透明度、缩放比例、位移等属性来自定义切换动画。

 /**
     * Custom swiper content transition animation.
     *
     * @param { SwiperContentAnimatedTransition } transition - custom content transition animation.
     * @returns { SwiperAttribute } the attribute of the swiper.
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @atomicservice
     * @since 12
     */
    customContentTransition(transition: SwiperContentAnimatedTransition): SwiperAttribute;

有一点需要注意的是,customContentTransition是自定义切换动画,但不会取消原有的动画,也就是说,我们在自定义动画的时候,原有的动画也是同步的执行的,为了让原有的动画不执行,我们可以设置主轴方向上负的位移(translate属性)来抵消页面滑动,这也是我们这个需求的主要实现方式。

准备数据

通过前边,我们已经知道,要实现前言中的需求,我们需要做的有两个动作,动作一,动态修改子元素的位移,我们可以通过translate属性来控制,动作二,动态改变子元素的层级权重,通过属性zIndex来设置。

基本UI如下,当然了,这只是一个简单的Demo案例,实际开发中,属性以及数据设置,要根据实际情况而定。

Swiper() {
        ForEach(this.backgroundColors, (backgroundColor: Color, index: number) => {
          Text(index.toString())
            .width("100%")
            .height("100%")
            .backgroundColor(backgroundColor)
            .textAlign(TextAlign.Center)
            .translate({ x: this.translateList[index] })
            .zIndex(this.zIndexList[index])
        })
      }
      .height(200)

以上的案例,我简单定义了三个数组,一个用于数据加载的backgroundColors,一个用于位移的translateList,最后一个是用于设置层级权重的zIndexList。

实现自定义切换动画

实现这个层叠滑动,实现customContentTransition方法之前,有一点需要知道,那就是我们目前是往左滑动还是往右滑动,因为在最后一个条目节点需要做特殊的逻辑判断,往左还是往右,我们可以通过onGestureSwipe方法中偏移量进行判断。

extraInfo.currentOffset小于0,往左,否则往右。

.onGestureSwipe((_: number, extraInfo: SwiperAnimationEvent) => {
        let currentOffset = extraInfo.currentOffset
        this.isScrollLeft = currentOffset < 0
      })

在customContentTransition方法中,我们正常执行滑动抵消即可,首先是在同组页面往左滑或往右完全滑出视窗外时,我们需要重置属性值,这是非常重要的。

如何把当前的滑动进行抵消呢,很简单,当往左滑动时,使用页面在主轴方向上的长度减去移动比例乘以主轴长度即可,代码如下:viewIndex是当前的索引。

  this.translateList[viewIndex] = proxy.mainAxisLength - proxy.position * proxy.mainAxisLength

往右滑动,直接负的移动比例乘以主轴长度,代码如下:

this.translateList[proxy.index] = -(proxy.position) * proxy.mainAxisLength

整个切换动画代码如下,在往左滑动的过程中,由于最后一个,需要动态切换为第一个索引,所以这里做了单独的逻辑处理。

.customContentTransition({
        timeout: 0,
        transition: (proxy: SwiperContentTransitionProxy) => {
          if (proxy.position <= 0 || proxy.position >= 1) {
            if (this.isScrollLeft) {
              if (proxy.index != this.translateList.length - 1) {
                this.translateList[proxy.index+1] = 0.0
                this.zIndexList[proxy.index+1] = 0
              }
            } else {
              this.translateList[proxy.index] = 0.0
              this.zIndexList[proxy.index] = 0
            }
          } else {
            if (this.isScrollLeft) {
              let viewIndex = proxy.index - 1
              if (viewIndex == -1) {
                this.translateList[this.translateList.length-1] =
                  proxy.mainAxisLength - proxy.position * proxy.mainAxisLength
                this.translateList[0] = 0
                this.zIndexList[0] = 1

              } else {
                this.translateList[viewIndex] = proxy.mainAxisLength - proxy.position * proxy.mainAxisLength
                this.zIndexList[viewIndex] = -1
              }
            } else {
              this.translateList[proxy.index] = -(proxy.position) * proxy.mainAxisLength
              this.zIndexList[proxy.index] = -1
            }
          }
        }
      })

通过以上的代码,一个层叠滑动效果就实现了,我们一起来看下最终的效果:

相关总结

customContentTransition不仅仅可以实现平移上的改变,很多的效果,我们都可以实现,比如放大缩小,旋转等等。

本文标签:HarmonyOS/ArkUI

animateControl v1.0.3 的功能特点: 1、完全兼容swiper的loop模式,完美支持swiper的嵌套。 2、完美支持animate.css,能够为页面中的任意元素添加动画。 3、完美支持为单个元素同时添加多个animate.css动画效果(无需进行HTML标签的嵌套),并且,可以随意控制这些动画效果的播放方式。 4、能够设置每一个动画效果的播放方式:同步播放、依次播放、循环播放。 5、能够设置每一个动画效果动画类型:进入动画、表演动画、退出动画(在animate.css的源码中,opacity值从0至100的是进入动画,没有opacity属性的是表演动画。opacity值从100到0的是退出动画)。 6、实现了排版效果动画效果动画控制的完美分离,极大的提高了开发效率,能够在很短的时间内实现复杂的动画效果控制。 7、animateControl 不与swiper结合时,可对网页中的其它元素添加动画效果,并进行控制。 8、能够让不懂JS代码的人,经过简单的学习,就能快速的制作HTML5+CSS3动态微网页。 9、相较于上一个版本,在 v1.0.3 中对整个核心代码进行了优化,大大提升了运行速率,使动画播放更流畅。 10、相较于上一个版本,在 v1.0.3 中简化了所有参数的名称,让使用变得更加简单(用更少的代码,就能实现更复杂的效果)。 11、本次发布的动画控制器文件为min版,大大减少了文件的体积(下载解压后,文件位置在:js/swiper.animate.min.js)。
<think>嗯,用户想在鸿蒙系统的Swiper组件中通过index切换时添加动画效果。首先,我需要回忆一下HarmonyOS的Swiper组件的相关文档和API。记得Swiper是ArkUI中的组件,用于实现轮播图或者页面滑动切换。 用户提到的是通过index切换时添加动画,可能需要使用ArkUI的动画能力。ArkUI支持属性动画、显式动画等。可能需要结合Swiper的index属性变化来触发动画效果。 然后,我需要考虑如何监听index的变化。或许可以使用状态管理,比如@State装饰器来跟踪当前索引,当索引变化时触发动画。另外,Swiper组件本身可能有change事件,可以在事件回调中执行动画逻辑。 接下来是动画的具体实现。比如,在切换时,可以让下一个页面从右侧滑入,当前页面向左滑出。这可以通过设置组件的平移变换(translate)属性,并结合animateTo方法来实现。需要确保动画的流畅性和正确的时间曲线,比如使用ease-in-out。 另外,可能还需要注意Swiper的默认动画效果是否会与自定义动画冲突。如果Swiper本身有切换动画,可能需要先禁用默认动画,或者将自定义动画与其结合。需要查阅文档确认Swiper是否支持关闭默认动画。 然后,代码结构方面,可能需要将Swiper的每个子组件包裹在一个自定义组件中,该组件能够响应index变化并应用动画。使用@Prop或@Link装饰器来传递动画所需的参数,比如偏移量或透明度。 还可能涉及到页面布局的问题,比如绝对定位,使得页面可以重叠,从而在切换时执行滑动效果。需要设置stack布局,并调整z-index确保正确的层叠顺序。 最后,测试代码是否正常工作,确保动画在index切换时正确触发,没有卡顿或闪烁的问题。可能需要调整动画的持续时间和延迟,以达到最佳效果。 总结步骤:监听index变化→触发动画→应用平移或透明度动画→调整布局和样式。同时参考官方文档中的动画示例,确保语法正确。</think>在HarmonyOS中,可以通过以下步骤为Swiper组件实现index切换动画效果: ### 一、基础实现方案 ```typescript // 启用Swiper动画属性 @Entry @Component struct AnimatedSwiper { @State currentIndex: number = 0 private controller: SwiperController = new SwiperController() build() { Swiper(this.controller) { ForEach([0, 1, 2], (index) => { Text(`Page ${index}`) .width('100%') .height('100%') .animation({ // 内置过渡动画 duration: 500, curve: Curve.Smooth }) }) } .index(this.currentIndex) .autoPlay(false) .onChange((index) => { this.currentIndex = index }) } } ``` 该方案通过Swiper内置的`animation`参数实现基础渐变过渡效果[^1]。 ### 二、自定义动画进阶方案 ```typescript @Component struct AnimatedPage { @Prop active: boolean private translateX: number = 0 build() { Column() { // 页面内容 } .width('100%') .height('100%') .translate({ x: this.active ? 0 : this.translateX }) .animation({ duration: 300, curve: Curve.EaseOut }) } } @Entry @Component struct CustomAnimatedSwiper { @State currentIndex: number = 0 private readonly pageCount: number = 3 build() { Stack() { ForEach(Array.from({length: this.pageCount}), (_, index) => { AnimatedPage({ active: index === this.currentIndex }) }) } .onClick(() => { this.currentIndex = (this.currentIndex + 1) % this.pageCount }) } } ``` 该方案通过Stack布局叠加页面,结合translate属性和条件渲染实现定制滑动动画。 ### 三、关键配置参数 1. **动画类型支持**: - 平移动画:`translate({x,y})` - 缩放动画:`scale({x,y})` - 旋转动画:`rotate({x,y,z})` - 透明度动画:`opacity` 2. **动画曲线配置**: ```typescript .animation({ duration: 400, curve: Curve.EaseInOut, // 支持Linear/EaseIn/EaseOut等 delay: 100 }) ``` 3. **复合动画实现**: ```typescript @keyframes slideEffect { from { translate: { x: 100% } opacity: 0 } to { translate: { x: 0 } opacity: 1 } } Text('Content') .animation(slideEffect) ``` ### 四、注意事项 1. 性能优化: - 单个动画时长建议控制在300-500ms - 避免同时激活超过3个动画 - 使用will-change属性声明动画元素: ```typescript .willChange(WillChange.Translate) ``` 2. 兼容性处理: ```typescript .animation({ duration: 300, curve: Curve.EaseOut, iterations: 1, playMode: PlayMode.Normal }) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员一鸣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值