【鸿蒙实战开发】ArkUI-动画曲线详解

201 篇文章 0 订阅
201 篇文章 0 订阅

概述

动画曲线是属性关于时间的变化函数,决定属性变化时产生动画的运动轨迹。某一时刻下动画曲线的斜率代表动画的速度,对应属性变化的快慢。一条优秀的动画曲线具备连续光滑、符合用户意图、符合物理世界客观规律的特点。开发者可结合用户的使用场景和意图,为动效选取合适的动画曲线。

根据动画曲线是否符合物理世界客观规律,可将其分为物理曲线(ArkUI当前提供了多种物理弹簧曲线)和传统曲线两种类型。相比于传统曲线,物理曲线产生的运动轨迹更加符合用户认知,有助于创造自然生动的动画效果,建议开发者优先使用物理曲线。

传统曲线基于数学公式,创造形状符合开发者预期的动画曲线。以三阶贝塞尔曲线为代表,通过调整曲线控制点,可以改变曲线形状,从而带来缓入、缓出等动画效果。对于同一条传统曲线,由于不具备物理含义,其形状不会因为用户行为发生任何改变,缺少物理动画的自然感和生动感。建议优先采用物理曲线创建动画,将传统曲线作为辅助用于极少数必要场景中。

传统曲线

ArkUI提供了贝塞尔曲线、阶梯曲线等传统曲线接口,开发者可参照插值计算进行查阅。

传统曲线的示例和效果如下:

1.  class MyCurve {
2.  public title: string;
3.  public curve: Curve;
4.  public color: Color | string;

6.  constructor(title: string, curve: Curve, color: Color | string = '') {
7.  this.title = title;
8.  this.curve = curve;
9.  this.color = color;
10.  }
11.  }

13.  const myCurves: MyCurve[] = [
14.  new MyCurve(' Linear', Curve.Linear, '#317AF7'),
15.  new MyCurve(' Ease', Curve.Ease, '#D94838'),
16.  new MyCurve(' EaseIn', Curve.EaseIn, '#DB6B42'),
17.  new MyCurve(' EaseOut', Curve.EaseOut, '#5BA854'),
18.  new MyCurve(' EaseInOut', Curve.EaseInOut, '#317AF7'),
19.  new MyCurve(' FastOutSlowIn', Curve.FastOutSlowIn, '#D94838')
20.  ]

22.  @Entry
23.  @Component
24.  export struct CurveDemo {
25.  @State dRotate: number = 0; // 旋转角度

27.  build() {
28.  Column() {
29.  // 曲线图例
30.  Grid() {
31.  ForEach(myCurves, (item: MyCurve) => {
32.  GridItem() {
33.  Column() {
34.  Row()
35.  .width(30)
36.  .height(30)
37.  .borderRadius(15)
38.  .backgroundColor(item.color)
39.  Text(item.title)
40.  .fontSize(15)
41.  .fontColor(0x909399)
42.  }
43.  .width('100%')
44.  }
45.  })
46.  }
47.  .columnsTemplate('1fr 1fr 1fr')
48.  .rowsTemplate('1fr 1fr 1fr 1fr 1fr')
49.  .padding(10)
50.  .width('100%')
51.  .height(300).margin({top:50})

53.  Stack() {
54.  // 摆动管道
55.  Row()
56.  .width(290)
57.  .height(290)
58.  .border({
59.  width: 15,
60.  color: 0xE6E8EB,
61.  radius: 145
62.  })

64.  ForEach(myCurves, (item: MyCurve) => {
65.  // 小球
66.  Column() {
67.  Row()
68.  .width(30)
69.  .height(30)
70.  .borderRadius(15)
71.  .backgroundColor(item.color)
72.  }
73.  .width(20)
74.  .height(300)
75.  .rotate({ angle: this.dRotate })
76.  .animation({ duration: 2000, iterations: -1, curve: item.curve, delay: 100 })
77.  })
78.  }
79.  .width('100%')
80.  .height(200)
81.  .onClick(() => {
82.  this.dRotate ? null : this.dRotate = 360;
83.  })
84.  }
85.  .width('100%')
86.  }
87.  }

0000000000011111111.20240807205234.56041049417221220819919994805682.gif

弹簧曲线

阻尼弹簧曲线(以下简称弹簧曲线)对应的阻尼弹簧系统中,偏离平衡位置的物体一方面受到弹簧形变产生的反向作用力,被迫发生振动。另一方面,阻尼的存在为物体振动提供阻力。除阻尼为0的特殊情况,物体在振动过程中振幅不断减小,且最终趋于0,其轨迹对应的动画曲线自然连续。

采用弹簧曲线的动画在达终点时动画速度为0,不会产生动画“戛然而止”的观感,以避免影响用户体验。

ArkUI提供了四种阻尼弹簧曲线接口。

  • springMotion:创建弹性动画,动画时长由曲线参数、属性变化值大小和弹簧初速度自动计算,开发者指定的动画时长不生效。

    springMotion不提供速度设置接口,速度通过继承获得,无需开发者指定。对于某个属性,如果当前存在正在运行的springMotion或者responsiveSpringMotion类型动画,新创建的弹簧动画将停止正在运行的动画,并继承其当前时刻的动画属性值和速度作为新建动画的初始状态。此外,接口提供默认参数,便于开发者直接使用。


    1.  function springMotion(response?: number, dampingFraction?: number, overlapDuration?: number): ICurve;
  • responsiveSpringMotion:是springMotion动画的一种特例,仅默认参数不同。一般用于跟手做成动画的场景,离手时可用springMotion创建动画,此时离手阶段动画将自动继承跟手阶段动画速度,完成动画衔接。

    当新动画的overlapDuration参数不为0,且当前属性的上一个springMotion动画还未结束时,response和dampingFraction将在overlapDuration指定的时间内,从旧动画的参数值过渡到新动画的参数值。

    1.  function responsiveSpringMotion(response?: number, dampingFraction?: number, overlapDuration?: number): ICurve;
  • interpolatingSpring:适合于需要指定初速度的动效场景,动画时长同样由接口参数自动计算,开发者在动画接口中指定的时长不生效。

    曲线接口提供速度入参,且由于接口对应一条从0到1的阻尼弹簧曲线,实际动画值根据曲线进行插值计算。所以速度也应该为归一化速度,其值等于动画属性改变的绝对速度除以动画属性改变量。因此不适合于动画起点属性值和终点属性值相同的场景,此时动画属性改变量为0,归一化速度不存在。

    1.  function interpolatingSpring(velocity: number, mass: number, stiffness: number, damping: number): ICurve;
  • springCurve:适合于需要直接指定动画时长的场景。springCurve接口与interpolatingSpring接口几乎一致,但是对于采用springCurve的动画,会将曲线的物理时长映射到指定的时长,相当于在时间轴上拉伸或压缩曲线,破坏曲线原本的物理规律,因此不建议开发者使用。

    1.  function springCurve(velocity: number, mass: number, stiffness: number, damping: number): ICurve;

关于弹簧曲线完整的使用示例和参考效果如下,开发者也可参考动画衔接,掌握使用responsiveSpringMotion和springMotion进行手势和动画之间的衔接。

弹簧曲线的示例代码和效果如下。

1.  import { curves } from '@kit.ArkUI';

3.  class Spring {
4.  public title: string;
5.  public subTitle: string;
6.  public iCurve: ICurve;

8.  constructor(title: string, subTitle: string, iCurve: ICurve) {
9.  this.title = title;
10.  this.iCurve = iCurve;
11.  this.subTitle = subTitle;
12.  }
13.  }

15.  // 弹簧组件
16.  @Component
17.  struct Motion {
18.  @Prop dRotate: number = 0
19.  private title: string = ""
20.  private subTitle: string = ""
21.  private iCurve: ICurve | undefined = undefined

23.  build() {
24.  Column() {
25.  Circle()
26.  .translate({ y: this.dRotate })
27.  .animation({ curve: this.iCurve, iterations: -1 })
28.  .foregroundColor('#317AF7')
29.  .width(30)
30.  .height(30)

32.  Column() {
33.  Text(this.title)
34.  .fontColor(Color.Black)
35.  .fontSize(10).height(30)
36.  Text(this.subTitle)
37.  .fontColor(0xcccccc)
38.  .fontSize(10).width(50)
39.  }
40.  .borderWidth({ top: 1 })
41.  .borderColor(0xf5f5f5)
42.  .width(80)
43.  .alignItems(HorizontalAlign.Center)
44.  .height(100)

46.  }
47.  .height(110)
48.  .margin({ bottom: 5 })
49.  .alignItems(HorizontalAlign.Center)
50.  }
51.  }

53.  @Entry
54.  @Component
55.  export struct SpringCurve {
56.  @State dRotate: number = 0;
57.  private springs: Spring[] = [
58.  new Spring('springMotion', '周期2, 阻尼0.25', curves.springMotion(1, 0.25)),
59.  new Spring('responsive' + '\n' + 'SpringMotion', '默认弹性跟手曲线', curves.responsiveSpringMotion(1, 0.25)),
60.  new Spring('interpolating' + '\n' + 'Spring', '初始速度10,质量1, 剛度228, 阻尼30', curves.interpolatingSpring(10, 1, 228, 30)),
61.  new Spring('springCurve', '初始速度10, 质量1, 剛度228, 阻尼30', curves.springCurve(10, 1, 228, 30))
62.  ];

64.  build() {
65.  Row() {
66.  ForEach(this.springs, (item: Spring) => {
67.  Motion({ title: item.title, subTitle: item.subTitle, iCurve: item.iCurve, dRotate: this.dRotate })
68.  })
69.  }
70.  .justifyContent(FlexAlign.Center).alignItems(VerticalAlign.Bottom)
71.  .width('100%')
72.  .height(437)
73.  .margin({ top: 20 })
74.  .onClick(() => {
75.  this.dRotate = -50;
76.  })
77.  }
78.  }

0000000000011111111.20240807205234.96164079386678860266826042515729.gif

写在最后

●如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
●点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
●关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
●更多鸿蒙最新技术知识点,请移步前往小编:https://gitee.com/

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值