背景
承接上篇和中篇,我们已经实现了整个页面的布局和功能逻辑,那么本章我们开始实现最后的动画效果吧。
继续实例
属性动画 (animation)
分析动效,当我们点击切换出发地和目的地按钮时,火车后面的刷新图标会进行旋转,这种组件的旋转通用属性变化时实现的动画效果,就是属性动画。
支持的属性包括width
、height
、backgroundColor
、opacity
、scale
、rotate
、translate
等。
我们本次需要修改旋转参数,先在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)
搞定,当前实例完成,看下最终效果吧。