一行代码给Button添加一个光标焦点动画:得着焦点按钮放大,失去焦点按钮恢复

news2024/11/17 19:37:19

当光标进入Button的时候,也就是Button得着焦点时,Button出现放大效果,失去焦点的时候,恢复原来的尺寸。
本例仅供学习交流之用

一、效果

按钮得着焦点,放大
按钮失去焦点,恢复
请添加图片描述

二、给按钮添加动效

得着焦点时,放大到原来的1.3倍,失去焦点后恢复。放大的时间0.3秒,恢复的时间0.3秒
在这里插入图片描述

btn.AddZoomEffect(1.3f,0.3f,0.3f);//得着焦点时,放大到原来的1.3倍,失去焦点后恢复。放大的时间0.3秒,恢复的时间0.3秒

在这里插入图片描述

三、给一堆button批量设置动效

在这里插入图片描述

四、附录:关键代码

1、核心脚本——包含扩展方法

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;  //唯一依赖的第三方包:https://github.com/Cysharp/UniTask
using System.Threading;
using System;
using UnityEngine.EventSystems;

/// <summary>
/// UI添加动效
/// </summary>
namespace UiAnimation
{
    /// <summary>
    /// 扩展方法 及其 核心方法
    /// </summary>
    public static class CoreLib
    {
        /// <summary>
        /// 给一个button扩展一个OnPointerEnterAddListener方法:添加一个OnPointerEnter的侦听
        /// </summary>
        /// <param name="button"></param>
        /// <param name="function"></param>
        public static void OnPointerEnterAddListener(this Button button, Func<UniTask> function)
        {
            Action<PointerEventData> unityAction = async (data) => { await function(); };

            EventTrigger trigger = button.gameObject.GetComponent<EventTrigger>();
            if (!trigger)
            {
                trigger = button.gameObject.AddComponent<EventTrigger>();
            }

            EventTrigger.Entry entry = new EventTrigger.Entry();
            entry.eventID = EventTriggerType.PointerEnter;
            entry.callback.AddListener(data => unityAction.Invoke((PointerEventData)data));
            trigger.triggers.Add(entry);
        }

        /// <summary>
        /// 给一个button扩展一个OnPointerEnterAddListener方法:添加一个OnPointerEnter的侦听
        /// </summary>
        /// <param name="button"></param>
        /// <param name="function"></param>
        public static void OnPointerEnterAddListener(this Button button, CancellationToken ctk, Func<CancellationToken, UniTask> function)
        {
            Action<PointerEventData> unityAction = async (data) => { await function(ctk); };

            EventTrigger trigger = button.gameObject.GetComponent<EventTrigger>();
            if (!trigger)
            {
                trigger = button.gameObject.AddComponent<EventTrigger>();
            }

            EventTrigger.Entry entry = new EventTrigger.Entry();
            entry.eventID = EventTriggerType.PointerEnter;
            entry.callback.AddListener(data => unityAction.Invoke((PointerEventData)data));
            trigger.triggers.Add(entry);
        }

        /// <summary>
        /// 给一个button扩展一个OnPointerExitAddListener方法:添加一个OnPointerExit的侦听
        /// </summary>
        /// <param name="button"></param>
        /// <param name="function"></param>
        public static void OnPointerExitAddListener(this Button button, Func<UniTask> function)
        {
            Action<PointerEventData> unityAction = async (data) => { await function(); };

            EventTrigger trigger = button.gameObject.GetComponent<EventTrigger>();
            if (!trigger)
            {
                trigger = button.gameObject.AddComponent<EventTrigger>();
            }

            EventTrigger.Entry entry = new EventTrigger.Entry();
            entry.eventID = EventTriggerType.PointerExit;
            entry.callback.AddListener(data => unityAction.Invoke((PointerEventData)data));
            trigger.triggers.Add(entry);
        }

        /// <summary>
        /// 光标进入的时候,button放大,失去光标的时候,缩小到原来的尺寸
        /// ****注意鼠标快速闪进闪出的逻辑处理:用任务控制****
        /// </summary>
        /// <param name="button"></param>
        /// <param name="scaleK">尺寸缩放的比例:在原尺寸的基础上放大或缩小</param>
        /// <param name="enterDuration">光标进入的动画时间</param>
        /// <param name="exitDuration">光标退出的动画时间</param>
        public static void AddZoomEffect(this Button button, float scaleK, float enterDuration, float exitDuration)
        {
            //初始化工作:异步任务管理,
            //    防止鼠标快速掠过或者快速胡乱点击,所以增加任务管理:
            //    1、【进入】的时候,把【退出】的任务先取消,
            //    2、【退出】的时候,把【进入】的任务先取消
            //TODO : ctsList的item超过指定个数的时候,设置item为null,remove多余的个数,也可以改用【队列】
            List<CancellationTokenSource> ctsList = new List<CancellationTokenSource>();
            var originalScale = button.transform.localScale;

            //光标进入的逻辑
            button.OnPointerEnterAddListener(async () =>
            {
                ctsList.ForEach(cts => cts.Cancel());//...需不需要set null 并clear list
                var cts = new CancellationTokenSource();
                ctsList.Add(cts);
                await button.transform.ZoomInAsync(scaleK, enterDuration, cts.Token);
            });

            //光标退出的逻辑
            button.OnPointerExitAddListener(async () =>
            {
                ctsList.ForEach(cts => cts.Cancel());//...需不需要set null 并clear list
                var cts = new CancellationTokenSource();
                ctsList.Add(cts);
                await button.transform.ZoomOutAsync(originalScale, exitDuration, cts.Token);
            });
        }

        /// <summary>
        /// 得着焦点的效果:UI尺寸变大
        /// </summary>
        /// <param name="transform">button对象</param>
        /// <param name="k">scale缩放系数:放大 or 缩小</param>
        /// <param name="duration">动画时间</param>
        /// <param name="ctk"></param>
        /// <returns></returns>
        public static async UniTask ZoomInAsync(this Transform transform, float k, float duration, CancellationToken ctk)
        {
            try
            {
                Vector3 startScale = transform.localScale;
                Vector3 targetScale = Vector3.one * k;
                var btn = transform.GetComponent<Button>();

                float elapse = 0f;
                while ((elapse < duration) && (!ctk.IsCancellationRequested))
                {
                    if (transform == null) return;            //场景销毁时,退出
                    transform.localScale = Vector3.Lerp(startScale, targetScale, elapse / duration);
                    Debug.Log($"{startScale}, {targetScale}, {Time.deltaTime / duration}");
                    await UniTask.Yield();
                    elapse += Time.deltaTime;
                }
            }
            catch (Exception e)
            {
                //Debug.Log($"\n 抛出一个OperationCanceledException");
                throw new OperationCanceledException();
            }
        }

        /// <summary>
        /// 失去焦点的效果:尺寸恢复
        /// </summary>
        /// <param name="transform">button对象</param>
        /// <param name="originalScale">原始scale</param>
        /// <param name="duration">动画时间</param>
        /// <param name="ctk"></param>
        /// <returns></returns>
        public static async UniTask ZoomOutAsync(this Transform transform, Vector3 originalScale, float duration, CancellationToken ctk)
        {
            try
            {
                Vector3 startScale = transform.localScale;
                Vector3 targetScale = originalScale;
                var btn = transform.GetComponent<Button>();

                float elapse = 0f;
                while ((elapse < duration) && (!ctk.IsCancellationRequested))
                {
                    if (transform == null) return;            //任务没取消,但是场景销毁 ——> 退出
                    transform.localScale = Vector3.Lerp(startScale, targetScale, elapse / duration);
                    Debug.Log($"{startScale}, {targetScale}, {Time.deltaTime / duration}");
                    await UniTask.Yield();
                    elapse += Time.deltaTime;
                }
            }
            catch (Exception e)
            {
                //Debug.Log($"\n 抛出一个OperationCanceledException");
                throw new OperationCanceledException();
            }
        }
    }
}

2、Demo脚本

给一堆button指定焦点效果

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace UiAnimation
{
    public class AddScaleAnimation : MonoBehaviour
    {
        /// <summary>
        /// 要添加动效的按钮
        /// </summary>
        [Header("要添加动效的按钮")]
        public List<Button> MyButtons = new List<Button>();

        /// <summary>
        /// scale缩放比例:enter时缩放,exit时恢复
        /// </summary>
        [Header("scale缩放比例")]
        public float k = 1.5f;

        /// <summary>
        /// 光标进入时的动效时间
        /// </summary>
        [Header("光标进入时的动效时间,单位秒")]
        public float enterAnimDuration;

        /// <summary>
        /// 光标退出时的动效时间
        /// </summary>
        [Header("光标退出时的动效时间,单位秒")]
        public float exitAnimDuration;

        // Start is called before the first frame update
        void Awake()
        {
            MyButtons.ForEach(btn =>
            {
                if (btn != null)
                {
                    //添加缩放效果
                    btn.AddZoomEffect(k,enterAnimDuration,exitAnimDuration);
                }
                else
                {
                    Debug.LogWarning($"注意:有面板参数没有提前设置!报错位置:{this.gameObject.name}->AddScaleAnimation");
                }
            });
        }
    }
}

五、思考

1、焦点的【进入】和【退出】用的是异步cancellationToken控制
2、你也可以添加其他的动能:旋转、抖动…
3、代码没有经过严格测试,谨慎使用。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

k8s中的基础概念

k8s可以从硬件和软件两方面来理解&#xff1a; 硬件&#xff1a; 1、节点&#xff08;Node&#xff09;&#xff1a;类似于手机、平板、电脑 2、集群&#xff08;Cluster&#xff09;&#xff1a;多个节点组合到一起 3、持久卷&#xff08;Persistent Volumes&#xff09;&…

微信小程序开发WebSocket通讯

官方文档说明&#xff1a;入口 WebSocket连接的链接只支持wss加密方式&#xff0c;且只能用域名的方式 该域名还要在微信公众平台的小程序中登记才能使用&#xff0c;开发->开发管理->服务器域名->修改 该域名要和https使用的一致 以域名地址&#xff1a;dtu.aab…

质量好洗地机有哪些?洗地机口碑榜

在很多人眼中&#xff0c;洗地机可能被简单地视为一种高价的拖把&#xff0c;但作为一个经验丰富的洗地机测评博主&#xff0c;我要强调洗地机在家务工作中的巨大价值。它不仅仅是一种清洁工具&#xff0c;更是集扫地、拖地、洗地以及擦干地板等多项功能于一身的强大设备。通过…

【软件测试】学习笔记-设计GUI自动化测试策略

这篇文章从“实战”这个角度展开&#xff0c;探讨实际的大型全球化电商网站的GUI自动化测试如何开展。这场实战&#xff0c;从以下两个方面展开&#xff1a; 测试策略如何设计&#xff1f;这一点&#xff0c;我会根据亲身经历的实际项目&#xff0c;和你探讨GUI测试的分层测试…

R730服务器做了raid的硬盘,插在R720上面可以用吗?

环境 戴尔R720 戴尔R730 问题描述 R730服务器做了raid的硬盘&#xff0c;插在R720上面可以用吗&#xff1f; 解决方案 1.直接在 R730服务器做了raid的硬盘&#xff0c;卸下来在插在r720上面使用 &#xff0c;读不到硬盘 2.前往R730服务器上面&#xff0c;去清除RAID配置的…

采集小红书笔记详情页的方法,大部分人都想得复杂了

一般来说&#xff0c;社交媒体都有列表页和详情页、个人主页三大页面&#xff0c;列表页一般包含搜索结果页、话题聚合页等等&#xff0c;详情页就是点开某一条笔记或者动态的具体页面&#xff1b; 小红书当然也不例外&#xff0c;比如下面这个话题聚合页&#xff1a; https:/…

csv文件查找工具(一)

自己写的小工具&#xff0c;查询csv文件前三列的数据互查&#xff0c; 主要目的是 组sql 需要这三个数据互查&#xff0c; 当然从数据库查的更快&#xff0c; 但数据库的连接需要验证权限&#xff0c;能自己搞&#xff0c;先自己搞 速度待优化&#xff0c;没时间搞了&#xff0…

【办公技巧】Word功能区灰色显示不能编辑,怎么破?

Word文档可以设置加密来保护文件禁止修改&#xff0c;但是在word文档中设置限制编辑功能时对它的作用是否有详细的了解呢&#xff1f;今天为大家介绍word限制编辑功能的作用以及忘记了限制编辑密码该如何解决。 设置限制大家应该都清楚&#xff0c;就是点击工具栏中的审阅 – …

微信小程序开发学习笔记《7》全局配置以及小程序窗口

微信小程序开发学习笔记《7》全局配置以及小程序窗口 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。全局配置官方文档 一、全局配置文件及常用的配置项 小程序根目录下的app.json 文件是小程序的全局配置文件。 常用的配置项如…

Java基础项目---飞机大战的简易实现

推荐阅读 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;一&#xff09; 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;二&#xff09; 文章目录 推荐阅读前言一、系统分析问题描述总体设计功能流程图 二、程序和算法的介绍FlyingOb…

DDNS-GO配置使用教程

环境&#xff1a;openwrt 下载地址&#xff1a;Releases jeessy2/ddns-go GitHub 下载 ssh至openwrt根目录&#xff0c;根据你的处理器选择要下载的版本&#xff0c;我是路由器&#xff0c;选择的是 ddns-go_5.7.1_linux_arm64.tar.gz wget github链接 安装 tar -zxvf…

Java面试题之JVM

Java面试题之JVM 1. JVM的组成部分及其作用&#xff1f;2. JVM的堆和栈的区别&#xff1f;3. 简述一下垃圾回收机制&#xff1f;(垃圾回收的原理&#xff1f;)4. 垃圾回收器都有什么&#xff1f;该怎么选择&#xff1f;5. 如何判断垃圾可以回收了&#xff1f;6. 垃圾回收算法有…

Redis原理篇(Dict的收缩扩容机制和渐进式rehash)

Dict&#xff08;即字典&#xff09; Redis是一种键值型数据库&#xff0c;其中键与值的映射关系就是Dict实现的。 Dict通过三部分组成&#xff1a;哈希表&#xff08;DictHashTable&#xff09;&#xff0c;哈希节点(DictEntry)&#xff0c;字典&#xff08;Dict&#xff09…

pgzrun 生命游戏制作过程详解

背景资料 康威生命游戏(Game of Life)是剑桥大学约翰何顿康威设计的计算机程序。 美国趣味数学大师马丁加德纳(Martin Gardner&#xff0c;1914-2010&#xff09;通过《科学美国人》杂志&#xff0c;将康威的生命游戏介绍给学术界之外的广大渎者&#xff0c;一…

互联网大厂职场各职级P6/P7和核心能力

目录 具体能力要求总结 具体能力要求 专业工匠 p5 被别人带领p6 独立完成项目全流程&#xff0c;指导 2-3 人 乐队指挥 p7 带行政团队 7-10 &#xff0c;项目团队&#xff0c;专项团队&#xff0c;复杂系统设计 1-3 个一般系统构成p8 领域专家 垂直 3 个团队 &#xff0c;横…

蓝桥杯基础知识3 memset()

蓝桥杯基础知识3 memset() #include <bits/stdc.h> using namespace std;int main(){int a[5]; //随机数for(int i 0;i < 5; i)cout << a[i] << \n;cout << \n;memset(a, 0, sizeof a); //0for(int i 0;i < 5; i)cout << a[i] << …

Proteus仿真stm32f103r6输出PWM/正弦波

资料下载地址&#xff1a;Proteus仿真stm32f103r6输出PWM/正弦波 一、仿真图 Proteus仿真stm32f103r6输出PWM/正弦波 二、程序 #include "pbdata.h"u16 fre; void RCC_Configuration(void); void GPIO_Configuration(void); void TIM3_Configuration();void Dela…

Kotlin程序设计(三)高级用法

Kotlin程序设计高级篇 在学习了前面的内容之后&#xff0c;相信各位小伙伴应该对Kotlin这门语言有了一些全新的认识&#xff0c;我们已经了解了大部分的基本内容&#xff0c;从本章开始&#xff0c;就是对我们之前所学的基本内容的进一步提升。 泛型 在前面我们学习了最重要…

【信息论与编码】【北京航空航天大学】实验二、哈夫曼编码【C语言实现】(下)图像编码压缩

实验2 哈夫曼编码&#xff08;下&#xff09;&#xff08;图像编码&#xff09; 实验简介&#xff1a; 本次实验为实验1&#xff1a;哈夫曼编码的后续补充&#xff0c;实验1见博客&#xff1a;实验一、哈夫曼编码【C语言实现】&#xff08;上&#xff09; 说明&#xff1a; 实…

Redis系列-15.Redis的IO多路复用原理解析

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…