ArkUI 开发实例:快五一了,做一个回家的订票动画界面(下)

背景

承接上篇和中篇,我们已经实现了整个页面的布局和功能逻辑,那么本章我们开始实现最后的动画效果吧。

继续实例

属性动画 (animation)

分析动效,当我们点击切换出发地和目的地按钮时,火车后面的刷新图标会进行旋转,这种组件的旋转通用属性变化时实现的动画效果,就是属性动画。

支持的属性包括widthheightbackgroundColoropacityscalerotatetranslate等。

我们本次需要修改旋转参数,先在build()前增加一个带状态的变量

less 复制代码
@State rotateValue: number = 0; // 火车图标旋转角度

然后为当前图标增加旋转角度的属性

scss 复制代码
Image("/pages/ComponentClassification/ExampleComponents/OneReserve/reverse.png")
  .width(40)
  .rotate({ angle: this.rotateValue })

最后增加动画,动画支持以下的参数对象:

名称 类型 是否必填 描述
duration number 动画持续时间,单位为毫秒。 默认值:1000
tempo number 动画播放速度,值越大动画播放越快,值越小播放越慢,为0时无动画效果。 默认值:1.0
curve Curve或ICurve或string 动画曲线。 默认值:Curve.EaseInOut
delay number 动画延迟播放时间,单位为ms(毫秒),默认不延时播放。
iterations number 动画播放次数。默认播放一次,设置为-1时表示无限次播放。设置为0时表示无动画效果。
playMode PlayMode 动画播放模式,默认播放完成后重头开始播放。 默认值:PlayMode.Normal
onFinish () => void 动画播放完成回调。 从API version 9开始,该接口支持在ArkTS卡片中使用。
finishCallbackType FinishCallbackType 在动画中定义onFinish回调的类型。 默认值:FinishCallbackType.REMOVED
expectedFrameRateRange ExpectedFrameRateRange 设置动画的期望帧率。

根据上上述表格,动画效果默认是1000ms,默认执行一次,默认无延迟,那么我们只需要将曲线修改成EaseOut即可,此时图片代码如下:

scss 复制代码
Image("/pages/ComponentClassification/ExampleComponents/OneReserve/reverse.png")
  .width(40)
  .rotate({ angle: this.rotateValue })
  .animation({
    curve: Curve.EaseOut,
  })

最后,回到当前按钮,设置当按钮点击后,旋转角度360度即可,按钮事件修改如下:

kotlin 复制代码
.onClick(() => {
  this.ifSwitch = !this.ifSwitch;
  this.ifStartSearch = false
  this.rotateValue = this.rotateValue + 360;
})

如上图所示,当前切换动画旋转效果已经实现,但是其他地方还是很生硬的,那么我们继续后续效果,接下来我们不再采用属性动画效果,而是通过转场方式实现。

组件内转场 (transition)

当用户组件出现和删除时,可以通过transition属性配置过度的动画效果,即为组件内转场动画。

其包含的参数对象如下:

接口名称 参数类型 是否静态函数 参数描述
opacity number 设置组件转场时的透明度效果,为插入时起点和删除时终点的值。 取值范围: [0, 1]
translate TranslateOptions 设置组件转场时的平移效果,为插入时起点和删除时终点的值。 -x:横向的平移距离。 -y:纵向的平移距离。 -z:竖向的平移距离。
scale ScaleOptions 设置组件转场时的缩放效果,为插入时起点和删除时终点的值。 -x:横向放大倍数(或缩小比例)。 -y:纵向放大倍数(或缩小比例)。 -z:当前为二维显示,该参数无效 。 - centerX、centerY指缩放中心点,centerX和centerY默认值是"50%",即默认以组件的中心点为缩放中心点。 - 中心点为(0, 0)代表组件的左上角。
rotate RotateOptions 设置组件转场时的旋转效果,为插入时起点和删除时终点的值。 -x:横向的旋转向量分量。 -y:纵向的旋转向量分量。 -z:竖向的旋转向量分量。 - centerX、centerY指旋转中心点,centerX和centerY默认值是"50%",即默认以组件的中心点为旋转中心点。 - 中心点为(0, 0)代表组件的左上角。 -centerZ指z轴锚点,即3D旋转中心点的z轴分量,centerZ默认值是0。 -perspective指视距,即视点到z=0平面的距离,perspective默认值是0。
move TransitionEdge 指定组件转场时从屏幕边缘滑入和滑出的效果,本质为平移效果,为插入时起点和删除时终点的值。
asymmetric appear: TransitionEffect, disappear: TransitionEffect 指定非对称的转场效果。 第一个参数指定出现的转场效果,第二个参数指定消失的转场效果。 如不通过asymmetric函数构造TransitionEffect,则表明该效果在组件出现和消失时均生效。
combine TransitionEffect 对TransitionEffect进行链式组合,以形成包含多种转场效果的TransitionEffect。
animation AnimateParam 指定该TransitionEffect的动画参数。 该参数只用来指定动画参数,其入参AnimateParam的onFinish回调不生效。 如果通过combine进行TransitionEffect的组合,前一TransitionEffect的动画参数也可用于后一TransitionEffect。

表格内容看似很复杂,特别是最好还有一些组合效果,但是我们先实战下简单的转场效果,实现出发地和目的地的对调,很明显这时候我们需要用到平移转场。

我们在出发地容器最后面增加转场效果,代码如下:

scss 复制代码
Column({ space: 5 }) {
  Text("信阳")
    .fontSize(20)
    .fontWeight(FontWeight.Medium)
    .fontColor("#132968")
  Text("XinYang")
    .fontSize(13)
    .fontColor("#132968")
}
.width(70)
.transition(
      TransitionEffect.translate({
        x: 200,
        y: 0,
        z: 0
      }).animation({ duration: 1000 })
)

代表出场和入场时从x方向200的位置偏移1000ms过来。

很明显效果还是很生硬,我们需要加入透明度效果,实现柔和的过渡,此时需要将透明度与位移动画进行融合。

增加透明度动画后代码如下:

less 复制代码
.transition(
  TransitionEffect.OPACITY.animation({ duration: 500, curve: Curve.LinearOutSlowIn })
    .combine(
      TransitionEffect.translate({
        x: 200,
        y: 0,
        z: 0
      }).animation({ duration: 1000 })
    )
)

很好此时动画效果很完美,那么我们在目的地也加上相应的代码即可。

目前按钮箭头还是很生硬,按钮之前我们专门切了两张图,这是因为我们需要实现垂直轴旋转的动画效果,所以我们在两张箭头图上增加沿着y轴旋转的转场180度的动画效果,当然也要加入透明度转场,此时代码如下:

less 复制代码
Button() {
  if (!this.ifSwitch) {
    Image("/pages/ComponentClassification/ExampleComponents/OneReserve/buttonArrow.png")
      .width(40)
      .transition(
        TransitionEffect.OPACITY.animation({ duration: 1000, curve: Curves.springCurve(10, 1, 268, 23) })
          .combine(
            TransitionEffect.rotate({ y: 1, angle: 180 }).animation({ duration: 1000 }))
      )
  }
  else {
    Image("/pages/ComponentClassification/ExampleComponents/OneReserve/buttonArrowReverse.png")
      .width(40)
      .transition(
        TransitionEffect.OPACITY.animation({ duration: 1000, curve: Curves.springCurve(10, 1, 268, 23) })
          .combine(
            TransitionEffect.rotate({ y: 1, angle: 180 }).animation({ duration: 1000 }))
      )
  }
}
.backgroundColor("#DEEBF9")
.width(40)
.onClick(() => {
  this.ifSwitch = !this.ifSwitch;
  this.ifStartSearch = false
  this.rotateValue = this.rotateValue + 360;
})

注:此时我们用了自定义曲线,需要引入曲线模板,在当前ets顶部增加如下代码即可:

javascript 复制代码
import Curves from '@ohos.curves'

最后,加下日期选择器切换到订票信息的动画效果了,

日期选择器有一个缩放的转场效果,我们在日期选择器下增加下面代码:

less 复制代码
.transition(
  TransitionEffect.OPACITY.animation({ duration: 1000, curve: Curves.springCurve(10, 1, 268, 23) }).combine(
    TransitionEffect.scale({
      x: 0,
      y: 0,
    }).animation({ duration: 700, curve: Curves.springCurve(10, 1, 268, 23), })
  )
)

此时日期选择器出现和消失时候就有缩放的动画了。

最后一步,为订票信息增加动画就好了,不过里面的信息有明显顺序,此时我们需要用到delay参数即可,为组件增加递增的delay效果。代码如下:

less 复制代码
Column() {
  Row({ space: 15 }) {
    Column({ space: 10 }) {
      Text("日期")
        .fontSize(15)
        .fontWeight(FontWeight.Normal)
        .fontColor("#A1A9C3")
      Text((this.selectedDate.getMonth() + 1).toString() + "月" + (this.selectedDate.getDate()).toString() + "日")
        .fontSize(17)
        .fontColor("#132968")
    }
    .width(80)
    .transition(
      TransitionEffect.OPACITY.animation({ duration: 1000, curve: Curves.springCurve(10, 1, 268, 23) })
        .combine(
          TransitionEffect.scale({
            x: 0,
            y: 0,
          }).animation({ duration: 700, curve: Curves.springCurve(10, 1, 268, 23), })
        )
    )

    Column({ space: 10 }) {
      Text("时间")
        .fontSize(15)
        .fontWeight(FontWeight.Normal)
        .fontColor("#A1A9C3")
      Text("10:08")
        .fontSize(17)
        .fontColor("#132968")
    }
    .width(80)
    .transition(
      TransitionEffect.OPACITY.animation({
        delay: 200,
        duration: 1000,
        curve: Curves.springCurve(10, 1, 268, 23)
      }).combine(
        TransitionEffect.scale({
          x: 0,
          y: 0,
        }).animation({ delay: 200, duration: 700, curve: Curves.springCurve(10, 1, 268, 23), })
      )
    )

    Column({ space: 10 }) {
      Text("车次")
        .fontSize(15)
        .fontWeight(FontWeight.Normal)
        .fontColor("#A1A9C3")
      Text("Z225")
        .fontSize(17)
        .fontColor("#132968")
    }
    .width(80)
    .transition(
      TransitionEffect.OPACITY.animation({
        delay: 400,
        duration: 1000,
        curve: Curves.springCurve(10, 1, 268, 23)
      }).combine(
        TransitionEffect.scale({
          x: 0,
          y: 0,
        }).animation({ delay: 400, duration: 700, curve: Curves.springCurve(10, 1, 268, 23), })
      )
    )
  }

  Row({ space: 15 }) {
    Column({ space: 10 }) {
      Text("车厢")
        .fontSize(15)
        .fontWeight(FontWeight.Normal)
        .fontColor("#A1A9C3")
      Text("07车")
        .fontSize(17)
        .fontColor("#132968")
    }
    .width(80)
    .transition(
      TransitionEffect.OPACITY.animation({
        delay: 600,
        duration: 1000,
        curve: Curves.springCurve(10, 1, 268, 23)
      }).combine(
        TransitionEffect.scale({
          x: 0,
          y: 0,
        }).animation({ delay: 600, duration: 700, curve: Curves.springCurve(10, 1, 268, 23), })
      )
    )

    Column({ space: 10 }) {
      Text("座位号")
        .fontSize(15)
        .fontWeight(FontWeight.Normal)
        .fontColor("#A1A9C3")
      Text("061号")
        .fontSize(17)
        .fontColor("#132968")
    }
    .width(80)
    .transition(
      TransitionEffect.OPACITY.animation({
        delay: 800,
        duration: 1000,
        curve: Curves.springCurve(10, 1, 268, 23)
      }).combine(
        TransitionEffect.scale({
          x: 0,
          y: 0,
        }).animation({ delay: 800, duration: 700, curve: Curves.springCurve(10, 1, 268, 23), })
      )
    )

    Column({ space: 10 }) {
      Text("票价")
        .fontSize(15)
        .fontWeight(FontWeight.Normal)
        .fontColor("#A1A9C3")
      Text("210.0元")
        .fontSize(17)
        .fontColor("#132968")
    }
    .width(80)
    .transition(
      TransitionEffect.OPACITY.animation({
        delay: 1000,
        duration: 1000,
        curve: Curves.springCurve(10, 1, 268, 23)
      }).combine(
        TransitionEffect.scale({
          x: 0,
          y: 0,
        }).animation({ delay: 1000, duration: 700, curve: Curves.springCurve(10, 1, 268, 23), })
      )
    )
  }
}
.justifyContent(FlexAlign.SpaceAround)
.height(165)

搞定,当前实例完成,看下最终效果吧。

相关推荐
yilylong3 小时前
鸿蒙(Harmony)实现滑块验证码
华为·harmonyos·鸿蒙
baby_hua3 小时前
HarmonyOS第一课——DevEco Studio的使用
华为·harmonyos
HarmonyOS_SDK4 小时前
融合虚拟与现实,AR Engine为用户提供沉浸式交互体验
harmonyos
- 羊羊不超越 -5 小时前
App渠道来源追踪方案全面分析(iOS/Android/鸿蒙)
android·ios·harmonyos
长弓三石7 小时前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
SameX9 小时前
鸿蒙 Next 电商应用安全支付与密码保护实践
前端·harmonyos
SuperHeroWu710 小时前
【HarmonyOS】键盘遮挡输入框UI布局处理
华为·harmonyos·压缩·keyboard·键盘遮挡·抬起
sanzk14 小时前
华为鸿蒙应用开发
华为·harmonyos
SoraLuna18 小时前
「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现
macos·ui·harmonyos
ClkLog-开源埋点用户分析19 小时前
ClkLog企业版(CDP)预售开启,更有鸿蒙SDK前来助力
华为·开源·开源软件·harmonyos