鸿蒙UI开发——使用动画曲线

ops/2024/11/15 2:02:24/

1、前 言

动画曲线是属性关于时间的变化函数,决定属性变化时产生动画的运动轨迹。某一时刻下动画曲线的斜率代表动画的速度,对应属性变化的快慢。

一条优秀的动画曲线具备连续光滑、符合用户意图、符合物理世界客观规律的特点。我们可结合用户的使用场景和意图,为动效选取合适的动画曲线。

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

2、阻尼弹簧曲线(物理曲线)

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

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

【之前的文章中,我们有简单弹簧曲线动画的基本使用,详见👉🏻 鸿蒙动画开发03——弹簧曲线动画】

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

1)springMotion

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

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

// 下面的方法是从这里导入使用👉🏻 import { curves } from '@kit.ArkUI'function springMotion(response?: number, dampingFraction?: number, overlapDuration?: number): ICurve;

参数解释如下:

决定弹簧复位的速度。默认值:0.55(秒),取值范围:(0, +∞),设置小于等于0的值时,按默认值0.55处理。

0表示无阻尼,一直处于震荡状态;大于0小于1的值为欠阻尼,运动过程中会超出目标值;等于1为临界阻尼;大于1为过阻尼,运动过程中逐渐趋于目标值。默认值:0.825(秒),取值范围:[0, +∞),设置小于0的值时,按默认值0.825处理。

  • response: 弹簧自然振动周期

  • dampingFraction:阻尼系数

  • overlapDuration弹性动画衔接时长。发生动画继承时,如果前后两个弹性动画response不一致,response参数会在overlapDuration时间内平滑过渡。默认值:0,单位:秒,取值范围:[0, +∞),设置小于0的值时,按默认值0处理。

使用示例如下:​​​​​​​

import { curves } from '@kit.ArkUI'curves.springMotion() // 创建一个默认弹性动画曲线curves.springMotion(0.5) // 创建指定response、其余参数默认的弹性动画曲线curves.springMotion(0.5, 0.6) // 创建指定response和dampingFraction、其余参数默认的弹性动画曲线curves.springMotion(0.5, 0.6, 0) // 创建三个参数均自定义的弹性动画曲线

2)responsiveSpringMotion

这是springMotion动画的一种特例(仅默认参数不同)。一般用于跟手做成动画的场景,离手时可用springMotion创建动画(离手阶段动画将自动继承跟手阶段动画速度,完成动画衔接)。

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

function responsiveSpringMotion(response?: number, dampingFraction?: number, overlapDuration?: number): ICurve;

使用方式和参数与springMotion类似,这里不再赘述。

3)interpolatingSpring

适合于需要指定初速度的动效场景,动画时长同样由接口参数自动计算,开发者在动画接口中指定的时长不生效。

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

function interpolatingSpring(velocity: number, mass: number, stiffness: number, damping: number): ICurve;

参数解释如下:

外部因素对弹性动效产生的影响参数,目的是保证对象从之前的运动状态平滑地过渡到弹性动效。该速度是归一化速度,其值等于动画开始时的实际速度除以动画属性改变值。取值范围:(-∞, +∞)

弹性系统的受力对象,会对弹性系统产生惯性影响。质量越大,震荡的幅度越大,恢复到平衡位置的速度越慢。取值范围:(0, +∞),设置的值小于等于0时,按1处理。

表示物体抵抗施加的力而形变的程度。刚度越大,抵抗变形的能力越强,恢复到平衡位置的速度越快。取值范围:(0, +∞),设置的值小于等于0时,按1处理。

  • velocity: 初始速度

  • mass质量

  • stiffness刚度

用于描述系统在受到扰动后震荡及衰减的情形。阻尼越大,弹性运动的震荡次数越少、震荡幅度越小。取值范围:(0, +∞),设置的值小于等于0时,按1处理。

  • stiffness阻尼

4)springCurve

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

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

效果参考

图片

代码如下:​​​​​​​

import { curves } from '@kit.ArkUI';class Spring {  public title: string;  public subTitle: string;  public iCurve: ICurve;  constructor(title: string, subTitle: string, iCurve: ICurve) {    this.title = title;    this.iCurve = iCurve;    this.subTitle = subTitle;  }}// 弹簧组件@Componentstruct Motion {  @Prop dRotate: number = 0  private title: string = ""  private subTitle: string = ""  private iCurve: ICurve | undefined = undefined  build() {    Column() {      Circle()        .translate({ y: this.dRotate })        .animation({ curve: this.iCurve, iterations: -1 })        .foregroundColor('#317AF7')        .width(30)        .height(30)      Column() {        Text(this.title)          .fontColor(Color.Black)          .fontSize(10).height(30)        Text(this.subTitle)          .fontColor(0xcccccc)          .fontSize(10).width(50)      }      .borderWidth({ top: 1 })      .borderColor(0xf5f5f5)      .width(80)      .alignItems(HorizontalAlign.Center)      .height(100)    }    .height(110)    .margin({ bottom: 5 })    .alignItems(HorizontalAlign.Center)  }}@Entry@Componentexport struct SpringCurve {  @State dRotate: number = 0;  private springs: Spring[] = [    new Spring('springMotion', '周期1, 阻尼0.25', curves.springMotion(1, 0.25)),    new Spring('responsive' + '\n' + 'SpringMotion', '弹性跟手曲线', curves.responsiveSpringMotion(1, 0.25)),    new Spring('interpolating' + '\n' + 'Spring', '初始速度10,质量1, 刚度228, 阻尼30', curves.interpolatingSpring(10, 1, 228, 30)),    new Spring('springCurve', '初始速度10, 质量1, 刚度228, 阻尼30', curves.springCurve(10, 1, 228, 30))  ];  build() {    Row() {      ForEach(this.springs, (item: Spring) => {        Motion({ title: item.title, subTitle: item.subTitle, iCurve: item.iCurve, dRotate: this.dRotate })      })    }    .justifyContent(FlexAlign.Center).alignItems(VerticalAlign.Bottom)    .width('100%')    .height(437)    .margin({ top: 20 })    .onClick(() => {      this.dRotate = -50;    })  }}

3、传统曲线

我们开发中常见的插值器:Linear、Ease、EaseIn、EaseOut、EaseInOut等,这些基于三阶贝塞尔曲线的数学公式,通过调整曲线形状来带来缓入缓出的效果,我们归为传统曲线。

对于同一条传统曲线,由于不具备物理含义,其形状不会因为用户行为发生任何改变,缺少物理动画的自然感和生动感。建议优先采用物理曲线创建动画,将传统曲线作为辅助用于极少数必要场景中。

ArkUI提供了贝塞尔曲线、阶梯曲线等传统曲线接口。支持的参数如下:

名称说明
Linear表示动画从头到尾的速度都是相同的。
Ease表示动画以低速开始,然后加快,在结束前变慢,cubic-bezier(0.25, 0.1, 0.25, 1.0)。
EaseIn表示动画以低速开始,cubic-bezier(0.42, 0.0, 1.0, 1.0)。
EaseOut表示动画以低速结束,cubic-bezier(0.0, 0.0, 0.58, 1.0)。
EaseInOut表示动画以低速开始和结束,cubic-bezier(0.42, 0.0, 0.58, 1.0)。
FastOutSlowIn标准曲线,cubic-bezier(0.4, 0.0, 0.2, 1.0)。
LinearOutSlowIn减速曲线,cubic-bezier(0.0, 0.0, 0.2, 1.0)。
FastOutLinearIn加速曲线,cubic-bezier(0.4, 0.0, 1.0, 1.0)。
ExtremeDeceleration急缓曲线,cubic-bezier(0.0, 0.0, 0.0, 1.0)。
Sharp锐利曲线,cubic-bezier(0.33, 0.0, 0.67, 1.0)。
Rhythm节奏曲线,cubic-bezier(0.7, 0.0, 0.2, 1.0)。
Smooth平滑曲线,cubic-bezier(0.4, 0.0, 0.4, 1.0)。
Friction阻尼曲线,cubic-bezier(0.2, 0.0, 0.2, 1.0)。

我们以场景的几个动画曲线作为示例,效果如下:

图片

代码如下:​​​​​​​

class MyCurve {  public title: string;  public curve: Curve;  public color: Color | string;  constructor(title: string, curve: Curve, color: Color | string = '') {    this.title = title;    this.curve = curve;    this.color = color;  }}const myCurves: MyCurve[] = [  new MyCurve(' Linear', Curve.Linear, '#317AF7'),  new MyCurve(' Ease', Curve.Ease, '#D94838'),  new MyCurve(' EaseIn', Curve.EaseIn, '#DB6B42'),  new MyCurve(' EaseOut', Curve.EaseOut, '#5BA854'),  new MyCurve(' EaseInOut', Curve.EaseInOut, '#317AF7'),  new MyCurve(' FastOutSlowIn', Curve.FastOutSlowIn, '#D94838')]@Entry@Componentexport struct CurveDemo {  @State dRotate: number = 0; // 旋转角度  build() {    Column() {      // 曲线图例      Grid() {        ForEach(myCurves, (item: MyCurve) => {          GridItem() {            Column() {              Row()                .width(30)                .height(30)                .borderRadius(15)                .backgroundColor(item.color)              Text(item.title)                .fontSize(15)                .fontColor(0x909399)            }            .width('100%')          }        })      }      .columnsTemplate('1fr 1fr 1fr')      .rowsTemplate('1fr 1fr 1fr 1fr 1fr')      .padding(10)      .width('100%')      .height(300).margin({top:50})      Stack() {        // 摆动管道        Row()          .width(290)          .height(290)          .border({            width: 15,            color: 0xE6E8EB,            radius: 145          })        ForEach(myCurves, (item: MyCurve) => {          // 小球          Column() {            Row()              .width(30)              .height(30)              .borderRadius(15)              .backgroundColor(item.color)          }          .width(20)          .height(300)          .rotate({ angle: this.dRotate })          .animation({ duration: 2000, iterations: -1, curve: item.curve, delay: 100 })        })      }      .width('100%')      .height(200)      .onClick(() => {        this.dRotate ? null : this.dRotate = 360;      })    }    .width('100%')  }}

http://www.ppmy.cn/ops/133742.html

相关文章

css实现div被图片撑开

固定好盒子的宽度&#xff0c;高度随传过来的图片大小决定 <div class"tab-con"> <img:src"concertInfo.detail"alt""> </div>.tab-con {margin-bottom: 20px;width: 700px;img {width: 700px;height: auto;object-fit: cont…

智能算法在金融分析中的应用:民锋科技的前沿探索

随着数据技术的发展&#xff0c;智能算法在金融市场中的应用逐渐增多&#xff0c;从模式识别到投资策略优化&#xff0c;智能算法正帮助金融从业者更高效地处理数据和做出决策。民锋科技积极探索智能算法的应用&#xff0c;通过技术创新为客户提供更精准的市场洞察和交易策略支…

Kafka 学习笔记

文章目录 1、背景 1、背景 Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;它可以处理消费者在网站中的所有动作流数据。 发布订阅&#xff08;Publish-Subscribe&#xff09;消息…

弄巧成拙的 PFC(Priority-based Flow Control)

先说几句车轱辘话&#xff0c;TCP 性能低&#xff0c;所以 RDMA&#xff0c;以太网丢包&#xff0c;所以 PFC&#xff0c;网卡不能太复杂&#xff0c;所以 GBN… HPC&#xff0c;AI 对吞吐&#xff0c;时延要求非常高&#xff0c;同时需要更多计算资源&#xff0c;而 TCP 处理…

保护Kubernetes免受威胁:容器安全的有效实践

安全并非“放之四海而皆准”的解决方案&#xff0c;相反地&#xff0c;它更多的是一个范围&#xff0c;受其应用的特定上下文的影响。安全领域的专业人士很少宣称什么产品是完全安全的&#xff0c;但总有方法可以实现更强的安全性。在本文中&#xff0c;我们将介绍各种方法来支…

MyBatis5-缓存

目录 一级缓存 二级缓存 MyBatis缓存查询的顺序 整合第三方缓存EHCache 一级缓存 一级缓存是 SqlSession 级别的&#xff0c;通过同一个 SqlSession 查询的数据会被缓存&#xff0c;下次查询相同的数据&#xff0c;就会从缓存中直接获取&#xff0c;不会从数据库重新访问 一…

蓝桥杯每日真题 - 第8天

题目&#xff1a;&#xff08;子2023&#xff09; 题目描述&#xff08;14届 C&C B组A题&#xff09; 解题思路&#xff1a; 该代码通过动态计算包含数字 "2023" 的子序列出现次数。主要思路是&#xff1a; 拼接序列&#xff1a;将1到2023的所有数字按顺序拆分…

反向代理模块开发

1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0c;将从服务器上得到的结果返回给客户端&#xff0c;此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说&#xff0c;反向代理就相当…