赞
踩
利用ArkUI组件不仅可以实现局部属性变化产生的属性动画,也可以实现父组件属性变化引起子组件产生过渡效果式的全局动画即显式动画。效果如图所示:
完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:
2.搭建烧录环境。
3.搭建开发环境。
本篇Codelab只对核心代码进行讲解。
- ├──entry/src/main/ets // 代码区
- │ ├──common
- │ │ └──constants
- │ │ └──Const.ets // 常量类
- │ ├──entryability
- │ │ └──EntryAbility.ts // 程序入口类
- │ ├──pages
- │ │ └──Index.ets // 动效页面入口
- │ ├──view
- │ │ ├──AnimationWidgets.ets // 动画组件
- │ │ ├──CountController.ets // 图标数量控制组件
- │ │ └──IconAnimation.ets // 图标属性动画组件
- │ └──viewmodel
- │ ├──IconItem.ets // 图标类
- │ ├──IconsModel.ets // 图标数据模型
- │ └──Point.ets // 图标坐标类
- └──entry/src/main/resources // 资源文件

页面入口由AnimationWidgets(动效组件)、CountController(动效图标数量控制组件)组成。
其中CountController通过Slider滑动控制quantity(动效图标数量);AnimationWidgets根据quantity展示相应数量的图标,点击组件按钮后通过在animateTo的event闭包函数中改变mainFlag状态,跟mainFlag相关的样式属性的变化都会产生动画效果,代码如下所示:
- // Index.ets
- @Entry
- @Component
- struct Index {
- @State quantity: number = Common.IMAGES_MIN;
- @Provide iconModel: IconsModel = new IconsModel(this.quantity, Common.OFFSET_RADIUS);
-
- build() {
- Column() {
- // 动画组件
- AnimationWidgets({
- quantity: $quantity
- })
- // 图标数量控制组件
- CountController({
- quantity: $quantity
- })
- }
- ...
- }
- }

CountController组件通过Slilder滑动控制动效图标的数量,最少3个图标,最多6个图标,示例代码如下所示:
- // CountController.ets
- @Component
- export struct CountController {
- @Link quantity: number;
-
- build() {
- Column() {
- Row() {
- Text($r('app.string.count'))
- .textStyle()
-
- Text(this.quantity)
- .textStyle()
- }
- ...
-
- Slider({
- value: this.quantity,
- min: Common.IMAGES_MIN,
- max: Common.IMAGES_TOTAL,
- step: 1,
- style: SliderStyle.InSet
- })
- .blockColor(Color.White)
- .selectedColor('#007DFF')
- .showSteps(true)
- .trackThickness($r('app.float.size_20'))
- .onChange((value: number) => {
- this.quantity = value;
- })
- ...
- }
- }
- }

点击AnimationWidgets组件的中心图标,调用animateTo方法,在event回调方法中改变状态,从而对组件本身产生缩放动画,和图标位置变化的动画效果,效果如下所示:
在animationTo的回调中修改mainFlag状态,所有跟mainFlag状态相关的属性变化都会产生过渡动画效果。代码如下所示:
- // AnimationWidgets.ets
- import { IconsModel } from '../viewmodel/IconsModel';
- import { IconAnimation } from './IconAnimation';
- import Common from '../common/constants/Const';
- import IconItem from '../viewmodel/IconItem';
-
- @Component
- export struct AnimationWidgets {
- @State mainFlag: boolean = false;
- @Link @Watch('onQuantityChange') quantity: number;
- @Consume iconModel: IconsModel;
-
- onQuantityChange() {
- this.iconModel.addImage(this.quantity);
- }
-
- aboutToAppear() {
- this.onQuantityChange();
- }
-
- animate() {
- animateTo(
- {
- delay: Common.DELAY_10,
- tempo: Common.TEMPO,
- iterations: 1,
- duration: Common.DURATION_500,
- curve: Curve.Smooth,
- playMode: PlayMode.Normal
- }, () => {
- this.mainFlag = !this.mainFlag;
- })
- }
-
- build() {
- Stack() {
- Stack() {
- ForEach(this.iconModel.imagerArr, (item: IconItem) => {
- IconAnimation({
- item: item,
- mainFlag: $mainFlag
- })
- }, (item: IconItem) => JSON.stringify(item.index))
- }
- .width(Common.DEFAULT_FULL_WIDTH)
- .height(Common.DEFAULT_FULL_HEIGHT)
- .rotate({
- x: 0,
- y: 0,
- z: 1,
- angle: this.mainFlag ? Common.ROTATE_ANGLE_360 : 0
- })
-
- Image(
- this.mainFlag
- ? $r("app.media.imgActive")
- : $r("app.media.imgInit")
- )
- .width($r('app.float.size_64'))
- .height($r('app.float.size_64'))
- .objectFit(ImageFit.Contain)
- .scale({
- x: this.mainFlag ? Common.INIT_SCALE : 1,
- y: this.mainFlag ? Common.INIT_SCALE : 1
- })
- .onClick(() => {
- this.iconModel.reset();
- this.animate();
- })
-
- Text($r('app.string.please_click_button'))
- .fontSize($r('app.float.size_16'))
- .opacity(Common.OPACITY_06)
- .fontColor($r('app.color.fontGrayColor'))
- .fontWeight(Common.FONT_WEIGHT_500)
- .margin({
- top: $r('app.float.size_100')
- })
- }
- .width(Common.DEFAULT_FULL_WIDTH)
- .layoutWeight(1)
-
- }
- }

组件的通用属性发生变化时,可以创建属性动画进行渐变,提升用户体验。示例效果如下所示:
当组件由animation动画属性修饰时,如果自身属性发生变化会产生过渡动画效果。本示例中当点击小图标时会触发自身clicked状态的变化,所有跟clicked相关的属性变化(如translate、rotate、scale、opacity)都会被增加动画效果。代码如下所示:
- // IconAnimation.ets
- export struct IconAnimation {
- @Link mainFlag: boolean;
- @ObjectLink item: IconItem;
-
- build() {
- Image(this.item.image)
- .width(Common.ICON_WIDTH)
- .height(Common.ICON_HEIGHT)
- .objectFit(ImageFit.Contain)
- .translate(
- this.mainFlag
- ? { x: this.item.point.x, y: this.item.point.y }
- : { x: 0, y: 0 }
- )
- .rotate({
- x: 0,
- y: 1,
- z: 0,
- angle: this.item.clicked ? Common.ROTATE_ANGLE_360 : 0
- })
- .scale(
- this.item.clicked
- ? { x: Common.SCALE_RATIO, y: Common.SCALE_RATIO }
- : { x: 1, y: 1 }
- )
- .opacity(this.item.clicked ? Common.OPACITY_06 : 1)
- .onClick(() => {
- this.item.clicked = !this.item.clicked;
- })
- .animation(
- {
- delay: Common.DELAY_10,
- duration: Common.DURATION_1000,
- iterations: 1,
- curve: Curve.Smooth,
- playMode: PlayMode.Normal
- }
- )
- }
- }

根据图标数量计算图标位置代码如下所示:
- // IconsModel.ets
- import Common from '../common/constants/Const';
- import IconItem from './IconItem';
- import Point from './Point';
-
- const TWO_PI: number = 2 * Math.PI;
-
- @Observed
- export class IconsModel {
- public imagerArr: Array<IconItem> = [];
- private num: number = Common.IMAGES_MIN;
- private radius: number;
-
- constructor(num: number, radius: number) {
- this.radius = radius;
- this.addImage(num);
- }
-
- public addImage(num: number) {
- this.num = num;
- if (this.imagerArr.length == num) {
- return;
- }
- if (this.imagerArr.length > num) {
- this.imagerArr.splice(num, this.imagerArr.length - num);
- } else {
- for (let i = this.imagerArr.length; i < num; i++) {
- const point = this.genPointByIndex(i);
- this.imagerArr.push(new IconItem(i, Common.IMAGE_RESOURCE[i], false, point));
- }
- }
-
- this.refreshPoint(num);
- }
-
- public refreshPoint(num: number) {
- for (let i = 0; i < num; i++) {
- this.imagerArr[i].point = this.genPointByIndex(i);
- }
- }
-
- public genPointByIndex(index: number): Point {
- const x = this.radius * Math.cos(TWO_PI * index / this.num);
- const y = this.radius * Math.sin(TWO_PI * index / this.num);
- return new Point(x, y);
- }
-
- public reset() {
- for (let i = 0; i < this.num; i++) {
- if (this.imagerArr[i].clicked) {
- this.imagerArr[i].clicked = false;
- }
- }
- }
- }

您已经完成了本次Codelab的学习,并了解到以下知识点:
为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频》
获取完整版白皮书方式请点击→《鸿蒙生态应用开发白皮书V2.0PDF》
一、入门必看
二、HarmonyOS 概念
三、如何快速入门?《鸿蒙基础入门学习指南》
四、开发基础知识
五、基于ArkTS 开发
更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册》
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。