【HarmonyOS开发实战】使用animation 和 animateTo来制作按钮动画(实现点击按钮释出更多小按钮)

如果你想在页面中添加按钮来实现页面跳转或者其他操作,又觉得过多的按钮太占地方,造成界面不美观。
那么我们可以将多个按钮“压缩”到一个按钮中,如下
在这里插入图片描述

在开始开发前,我们先了解一下animation和animateTo的区别。

animation(属性动画)

属性动画是用于在组件的某些通用属性变化时实现渐变过渡效果的动画。支持的属性包括:width,height,backgroundColor,opacity,scale(缩放),rotate(旋转),translate(移动)等

布局类改变宽高的动画,内容都是直接到终点状态,例如文字、Canvas的内容等,如果要内容跟随宽高变化,可以使用renderFit属性(组件内容填充方式)配置。

animation(value: AnimateParam)

使用方法

/属性参数
  ...
  ...
  ...
/

.animation({
    duration:2000,
    curve:Curve.EaseOut
    ...
    ...
})

当在animation前面的属性发生变化时,组件便会按照animation中的配置进行动画演出。

注意:属性动画只对写在animation前面的属性生效,且对组件构造器的属性不生效

Column({space:..})  <-此为组件构造器,animation对其不生效

当需要对单一或多个属性进行独立动画片配置,属性动画(animation更好一些),这样每个属性可以有不同的动画参数,适合精细化控制界面元素的动画效果。

animateTo(显式动画)

显式动画是通过在代码中显式地定义动画效果来实现视图的动态变化。其用法与animation大体相似,多了闭包代码以实现由于闭包代码导致的状态变化插入过渡动效。

animateTo(value: AnimateParam, event: () => void): void

使用方法

animateTo({  
    duration: 2000,
    curve: Curve.EaseOut,
    iterations: 3,
    playMode: PlayMode.Normal,
    onFinish: () => {
        console.info('play end')
        }
        }, () => { //在这里修改组件属性,来形成动画效果
              this.widthSize = 150
              this.heightSize = 60
            })

常用参数配置一览(animateTo和animation共享):

  • duration(动画持续时间)
  • tempo(动画播放速度)
  • curve(动画曲线)
  • delay(动画延迟播放时间)
  • iterations(动画播放次数)
  • playMode(动画播放模式)
  • onFinish(动画播放完成回调)

当需要对一系列界面变化进行统一动画处理时,特别是涉及条件判断、数组元素的动态增删等非直接属性变更的情况,使用显示动画(animateTo)更好一些。

总得来说,属性动画(animation)更适用于简单的属性变化动画,而显式动画(animateTo)则适用于更复杂的动画场景,包括组件的增删和属性的批量变化。开发者可以根据具体的动画需求和场景选择适合的动画实现方式。

实战开发

在这里插入图片描述

开发所需文件一览

viewModel层

首先我们先从viewModel层开始做起,定义动画元件的各项属性

Point——坐标类

//设置坐标位置

export class Point {
  public x: number = 0
  public y: number = 0

  constructor(x:number,y:number) {
    this.x = x
    this.y = y
  }
}

这里不用多说了,就是简单的xy坐标定义

IconItem——单个元件类

import {Point} from './Point'

//设定图标属性

export class IconItem {
//这里可以根据需要定义多个属性
  index: number = 0  //下标
  clicked: boolean = false   //是否被点击
  image: Resource = $r('app.media.testicon')
  point: Point = new Point(0,0)   //坐标
  name: string = ''  //自定义属性

  constructor(index: number,image:Resource,clicked: boolean,point: Point,name:string) {
    this.index = index
    this.image = image
    this.clicked = clicked
    this.point = point
    this.name = name

在该类中我们定义单个动画元件的属性,其中最重要的就是坐标,因为我们做的是一个移动图标显示的动画,而实现这个效果就需要坐标改变来实现。

IconModel_——核心元件类,定义元件集及其配置方法

import {IconItem} from './IconItem'
import {Point} from './Point
import Constant from '../Constant/Constant'


export class IconModel {
  public imagerArr: Array<IconItem> = [];
  private num: number = 1;
  private radius: number;   //半径,因为是按圆周展开
  private nameList: Array<string>  //这里定义数组,方便元件创建时加入

  constructor(num: number, radius: number,nameList:Array<string>) {
    this.radius = radius;
    this.addImage(num,nameList);  //相当于创建元件
    this.nameList = nameList ;
  }

//添加元件
  public addImage(num: number,nameList: Array<string>) {
    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, Constant.IMAGE_RESOURCE[i], false, point,nameList[i]));
      }
    }

    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 angle =  -Math.PI*5/6+Math.PI * index / this.num;
    const x = this.radius * Math.cos(angle);
    const y = this.radius * Math.sin(angle);
    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;
      }
    }
  }
}

在IconsModel类中我们设定了元件集的属性,还有内置的方法用于创建元件集。

创建元件集的基本逻辑就是:

  • 在Constant中设定统一的元件图像(我这里放了png图片,大家可以根据个人喜好放置喜欢的图标)

    const IMAGE_ARR = [
      $r('app.media.testicon'),
      $r('app.media.testicon'),
      $r('app.media.testicon'),
      $r('app.media.testicon'),
      ...放多少都可以
    ]
    
    export default class Constant {
      static readonly IMAGE_RESOURCE: Resource[] = IMAGE_ARR
    }
    
    
  • 在IconsModel接口中构造了三个参数,num,radius,nameList,其中num就是点击释出的元件数,(nameList是每个元件在定义时的标记,看个人需求添加),在addImage方法中根据num和nameLIst来创建对应数量的元件并加入到元件集中。

    public addImage(num: number,nameList: Array<string>) {
        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, Constant.IMAGE_RESOURCE[i], false, point,nameList[i]));
          }
        }
    
        this.refreshPoint(num);
      }
    
  • 在addImage方法中调用genPointByIndex方法,确定每个元件释出后的坐标值

      public genPointByIndex(index: number): Point {
        const angle =  -Math.PI*5/6+Math.PI * index / this.num;
        const x = this.radius * Math.cos(angle);
        const y = this.radius * Math.sin(angle);
        return new Point(x, y);
      }
    

    这里的计算式生成的是点击处正上方均匀分布三个元件的计算式,如果需要调整位置可以在这里更改计算式。

以上,动画元件集就定义好了。

component层

属性动画——IconAnimation

在这一部分我们做单个元件的动画行为,因为是根据单个元件的位置属性进行移动动画的播放,所以我们使用属性动画animation来制作。

import {IconItem} from '../viewmodel/IconItem'
import Constants from '../Constant/Constant'
import {Point} from '../viewmodel/Point'

@Component

export struct IconAnimation {
  @Link mainFlag: boolean;     //我们根据这个来判断元件是否释出
  @State PointTest :Point = new Point(0,0)
  @State item: IconItem = new IconItem(1, $r('app.media.testicon'),false,this.PointTest,"测试");


  build() {
    Stack(){

      Image(this.item.image)
        .width(58)
        .height(58)
        .objectFit(ImageFit.Contain)
        //移动元件位置(使用条件判断语句根据mainflag状态来控制移动)
        .translate(this.mainFlag ? { x: this.item.point.x, y: this.item.point.y } : { x: 0, y: 0 })
        .onClick(() => {
        //这里可以设计点击事件,根据个人需要是否要定义点击状态
          this.item.clicked = !this.item.clicked;
          console.log("名字是"+this.item.name)
        })

    }
    .animation(
      {
          delay: 10,
          duration: 1000,
          iterations: 1,
          curve: Curve.Smooth,
          playMode: PlayMode.Normal
      })
      //参数设置
  }
}

因为这个动画要实现按钮的出现与消失,也就是我们要控制每次点击时按的钮释出与放回。因此我们定义一个变量mainflag来实现该功能。

显式动画——AnimationWidgets

做完了单个元件的动画,我们现在开始整合多个元件。由于是控制多个元件,所以这里我们用到了animateTo

import {IconItem} from '../viewmodel/IconItem'
import {IconModel} from '../viewmodel/IconModel'
import {IconAnimation} from '../components/IconAnimation'

@Component
export struct AnimationWidgets{
  @State mainFlag: boolean = false;  //是否完成360度旋转
  @State private ButtonImage: Resource = $r('app.media.testicon')

  @State private iconModel: IconModel = new IconModel(1,2,['']);

  animate() {
    //动画属性
    animateTo(
      {
        delay: 10,
        tempo: 0.68,
        iterations: 1,
        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("100%")
      .height("100%")

      Image(this.ButtonImage)
        .width(64)
        .height(64)
        .objectFit(ImageFit.Contain)

        .onClick(() => {
          this.animate();
          console.log("已播放动画")
        })

      Text('点击按钮')
        .fontSize( 16)
        .opacity(6)
        .fontWeight(FontWeight.Medium)
        .margin({
          top: 100
        })
    }
    .width("100%")
    .layoutWeight(1)
  }

}

调用方法

import {IconModel} from '../viewmodel/IconModel'
import {AnimationWidgets} from "../components/AnimationWidgets"

@Preview
@Component
export struct MainPage {

  @State
  private IconNum : number = 3
  @State
  PageNameList: Array<string> = ["测试","测试","测试"]


  IconList: IconModel = new IconModel(this.IconNum,45,this.PageNameList)

  build() {
    Column(){
      AnimationWidgets({iconModel:this.IconList})
    }
    .width("100%")
    .height("100%")
    .backgroundColor(Color.White)
  }

}

这样我们就实现了点击按钮进行小按钮的释出。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/923034.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

国家级资质!同驭汽车获得CNAS实验室认证

近日&#xff0c;同驭汽车科技顺利通过中国合格评定国家认可委员会&#xff08;简称CNAS&#xff09;评审&#xff0c;获得《中国合格评定国家认可委员会实验室认可证书》。这标志着同驭已建立国际标准的实验室管理体系&#xff0c;产品的试验与检测技术能力达到了国际认可的准…

选择使用whisper.cpp进行语音转文字

需要将一些wav格式的语音文件转成文字&#xff08;ASR&#xff0c;STT&#xff09;&#xff0c;接到这个任务后&#xff0c;首先上网搜索有没有现成免费的工具或服务可以使用。常用的关键字如“语音转文字 免费 在线”。 搜到的很多野鸡网站&#xff0c;都可以免注册免费提供短…

消息称三星正与 OpenAI 洽谈,有望令 Galaxy AI 整合ChatGPT,三星都要和chatgpt合作了,你会使用chatgpt了吗?

还不知道怎么订阅chatgpt4.o和国外app服务的同学&#xff0c;可以看这里&#xff1a;WildCard官方平台订阅chatgpt 11 月 25 日消息&#xff0c;金融分析师 Dan Nystedt 在 X 平台透露称 OpenAI 正在与三星电子洽谈合作计划&#xff0c;讨论将其 ChatGPT 引入三星 Galaxy AI 的…

candence: 常用的一些命令: Move / Mirror / Rotate / Spain / Fix / unFix / Flipdesign

常用的一些命令 一、 Move 移动 一个可移动一个&#xff0c;也可多个 移动器件 二、 Mirror 镜像 Mirror 就是top 和 bottom 层的器件进行相互转换 三、 Rotate 旋转 移动过程中旋转 四、旋转 Spain 不能在移动中旋转 可以一次旋转一个&#xff0c;也可多个 一次旋转…

【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程

【深度学习】【RKNN】【C】模型转化、环境搭建以及模型部署的详细教程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】【RKNN】【C】模型转化、环境搭建以及模型部署的详细教程前言模型转换--pytorch转rknnpytorch转onnxonnx转rkn…

Hadoop3.3.6集群安装

Hadoop3.3.6 三节点集群安装 准备工作 准备三台机器&#xff0c;大小为4c8g&#xff0c;主节点为 8c16g。并需要保证网络连通性&#xff0c;每台机器都相互ping一下 1、关闭网络防火墙 # 查看网络防火墙状态 sudo systemctl status firewalld # 立即停止 firewalld sudo sy…

计算机网络-GRE(通用路由封装协议)简介

昨天我们学习了VPN的基本概念&#xff0c;虚拟专用网络在当前企业总部与分支间广泛使用。常用的划分方法为基于协议层次有GRE VPN、IPSec VPN、L2TP VPN、PPTP VPN、SSL VPN等。其实我有考虑该怎么讲&#xff0c;因为在IP阶段好像虚拟专用网络讲得不深&#xff0c;在IE的阶段会…

Android 应用测试的各种环境问题记录(Instrumentation测试)

报错记录 failed to configure packages targetSdkVersion&#xff08;未解决&#xff09; failed to configure com.demo.test.SettingsActivityTest.testOnCreate_withNullSavedInstanceState: Package targetSdkVersion34 > maxSdkVersion32 java.lang.IllegalArgumentE…

计算机网络复习笔记(湖科大教书匠)

课程链接&#xff1a;【计算机网络微课堂&#xff08;有字幕无背景音乐版&#xff09;】 https://www.bilibili.com/video/BV1c4411d7jb/?p61&share_sourcecopy_web&vd_sourcecd12864239c2976e9f2bce4b307393f0 一、基础概念 信息交换方式 电路交换 电话交换机接通…

探索运维新视界,CMDB的3D机房功能深度解析

在数字化转型的浪潮中&#xff0c;数据中心作为企业信息架构的核心&#xff0c;其高效、智能的管理成为了企业竞争力的关键因素之一。3D机房作为这一趋势下的创新产物&#xff0c;正逐步改变着传统机房运维的面貌。本文将结合乐维CMDB&#xff0c;深入探讨3D机房的功能细节、应…

时序论文25|ShapeFormer: 用于多变量时间序列分类的Shapelet Transformer

论文标题&#xff1a;ShapeFormer: Shapelet Transformer for Multivariate Time Series Classification 论文链接&#xff1a;https://arxiv.org/abs/2405.14608 代码链接&#xff1a;https://github.com/xuanmay2701/shapeformer. 前言 本文面向的任务是多元时间序列分类…

Unity 设计模式-状态模式(State Pattern)详解

状态模式&#xff08;State Pattern&#xff09; 状态模式&#xff08;State Pattern&#xff09; 是一种行为型设计模式&#xff0c;它允许一个对象在其内部状态发生改变时改变其行为。状态模式将与状态相关的行为封装在独立的状态类中&#xff0c;系统在运行时根据状态的变化…

C 语言复习总结记录六

C 语言复习总结记录六 一 指针 指针是什么 &#xff1f; 指针是内存中一个最小单元的编号&#xff0c;也就是地址 指针通常是指指针变量&#xff0c;用来存放内存地址的变量 指针变量 &#xff1a;通过 &&#xff08;取地址操作符&#xff09;取出变量的内存起始地址&…

Elasticsearch中的节点(比如共20个),其中的10个选了一个master,另外10个选了另一个master,怎么办?

大家好&#xff0c;我是锋哥。今天分享关于【Elasticsearch中的节点&#xff08;比如共20个&#xff09;&#xff0c;其中的10个选了一个master&#xff0c;另外10个选了另一个master&#xff0c;怎么办&#xff1f;】面试题。希望对大家有帮助&#xff1b; Elasticsearch中的节…

YOLOv8改进,YOLOv8引入SE注意机制+C2fCIB模块,二次创新C2f结构

摘要 # 理论介绍 SE 注意力机制是一种提升卷积神经网络(CNN)性能的模块。SE 更关注重要的特征图,增强了网络的表现,同时仅增加了较少的参数。SE 机制包含两个主要步骤: Squeeze (压缩):对所有特征图进行全局平均池化,生成一个通道描述符。Excitation (激励):将通道描述…

IEC61850实现方案和测试-2-UCA

IEC61850实现方案和测试-1作为介绍实现方案和测试的第二篇文章&#xff0c;后续会继续更新&#xff0c;欢迎关注。 第一篇是&#xff1a;IEC61850实现方案和测试-1-CSDN博客 UCA详细测试用例下载&#xff1a; 链接: https://pan.baidu.com/s/1TTMlYRfzKITgrkWwwtcrDg 提取码:…

Ubuntu20.04运行DM-VIO

文章目录 论文信息环境配置编译运行参考 论文信息 论文题目&#xff1a;论文地址&#xff1a;发表期刊&#xff1a;开源代码&#xff1a; 环境配置 将项目中Cmakelists.txt中C 和 opencv版本修改下 C 使用 14 opencv使用4 编译 按照官网即可 cd dm-vio mkdir build cd bui…

ElasticSearch学习篇18_《检索技术核心20讲》LevelDB设计思想

目录 一些常见的设计思想以及基于LSM树的LevelDB是如何利用这些设计思想优化存储、检索效率的。 几种常见的设计思想 索引和数据分离减少磁盘IO读写分离分层思想 LevelDB的设计思想 读写分离设计分层设计与延迟合并LRU缓存加速检索 几种常见设计思想 索引与数据分离 索引…

《用 Python 和 Tkinter 打造惊喜弹窗小应用教程》

在日常使用电脑的过程中&#xff0c;偶尔来点小惊喜总是能让人心情愉悦。今天&#xff0c;我要和大家分享一段有趣的 Python 代码&#xff0c;它借助 Tkinter 库创建一系列随机位置弹出的温馨提示窗口&#xff0c;给人带来意想不到的惊喜效果。下面就让我们一起来深入了解这段代…

JeecgBoot 实现Table列的动态加载

需要在vue文件中引入 render //这个是显示图片的&#xff0c;如果是文字不需要 import { render } from "//utils/common/renderUtils"; 注册table 时 //注册table时添加 getRawDataSource, setColumns const [registerTable, { reload, setProps, setLoading, u…