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

当光标进入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、代码没有经过严格测试,谨慎使用。

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

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

相关文章

vbs读取数据库值前端FlexGrid前导0出不来的原因

vbs读取数据库值前端FlexGrid前导0出不来的原因 原因 系统设置问题 解决 修改系统默认数值显示&#xff1a; 1&#xff09;控制面板找到“区域”&#xff0c;点击“更改日期、时间和数字模式”&#xff0c;在弹出窗口点击“其他设置” 2&#xff09;在数字一栏中的“显示前…

Java的helloworld、IDEA一些快捷键、导入模块

一、Java的helloworld IDEA管理Java程序的结构 1.project&#xff08;项目、工程&#xff09; 2.moudule&#xff08;模块&#xff09; 3.package&#xff08;包&#xff09; 4.class&#xff08;类&#xff09; 上级包含多个下级&#xff0c;开发程序也是创建工程再创建…

【Web】CTFSHOW PHP文件包含刷题记录(全)

温故知新。 目录 web78 web79 web80 web81 web82 web83 web84 web85 web86 web87 web88 web78 伪协议base64编码直接读出文件内容就行 ?filephp://filter/convert.base64-encode/resourceflag.php web79 一眼data伪协议包含php脚本 ?filedata://text/plain,<…

2020年认证杯SPSSPRO杯数学建模C题(第二阶段)抗击疫情,我们能做什么全过程文档及程序

2020年认证杯SPSSPRO杯数学建模 C题 抗击疫情&#xff0c;我们能做什么 原题再现&#xff1a; 2020 年 3 月 12 日&#xff0c;世界卫生组织&#xff08;WHO&#xff09;宣布&#xff0c;席卷全球的冠状病毒引发的病毒性肺炎&#xff08;COVID-19&#xff09;是一种大流行病。…

JAVA微信营销平台源码带使用文档

JAVA微信营销平台源码带使用文档 开发环境&#xff1a; Eclipse版本&#xff1a;Eclipse Java EE IDE for Web Developers. JDK版本&#xff1a;jdk7 Tomcat版本&#xff1a;tomcat7 Mysql&#xff1a;mysql5.0以上

Redis 为什么要分16个库

目录 一. 前言 二. 16 个数据库的由来 三. 正解 Redis 数据库概念 四. 集群环境下的 Redis 实例 五. 总结 一. 前言 在实际的项目中&#xff0c;Redis 常被用作缓存、分布式锁、消息队列等的解决方案。但是在搭建好Redis 服务后&#xff0c;Redis 默认创建了16个数据库&am…

虹科新闻丨LIBERO医药冷链PDF温度计完成2024年航空安全鉴定,可安全空运!

来源&#xff1a;虹科环境监测技术 虹科新闻丨LIBERO医药冷链PDF温度计完成2024年航空安全鉴定&#xff0c;可安全空运&#xff01; 原文链接&#xff1a;https://mp.weixin.qq.com/s/XHT4kU27opeKJneYO0WqrA 欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; 虹科LIBE…

php内置函数-文件包含的函数

目录 1.include 2.require 3.include_once 4. require_once 1.include 可以将别的文件直接引用过来&#xff08;被引用的文件含有打印代码的话&#xff0c;会直接打印&#xff09;&#xff0c;如果失败了&#xff0c;会返回一条警告&#xff0c;文件会继续执行下去&#…

超越5大最先进的文本到视频系统!MagicVideo-V2:多阶段高保真视频生成框架(字节)

本项工作介绍了MagicVideo-V2&#xff0c;将文本到图像模型、视频运动生成器、参考图像embedding模块和帧内插模块集成到端到端的视频生成流程中。由于这些架构设计的好处&#xff0c;MagicVideo-V2能够生成具有极高保真度和流畅度的美观高分辨率视频。通过大规模用户评估&…

Python办公自动化 – 自动化文本翻译和Oracle数据库操作

Python办公自动化 – 自动化文本翻译和Oracle数据库操作 以下是往期的文章目录&#xff0c;需要可以查看哦。 Python办公自动化 – Excel和Word的操作运用 Python办公自动化 – Python发送电子邮件和Outlook的集成 Python办公自动化 – 对PDF文档和PPT文档的处理 Python办公自…

11.3、信赖域策略优化算法TRPO强化学习-运用实践

基于LunarLander登陆器的TRPO强化学习&#xff08;含PYTHON工程&#xff09; TRPO强化学习算法主要分为3个部分&#xff0c;分别介绍其理论、细节、实现 本文主要介绍TRPO的理论和代码的对应、实践 TRPO系列&#xff08;TRPO是真的复杂&#xff0c;全部理解花费了我半个月的…

C++算法学习心得五.二叉树(3)

1.合并二叉树&#xff08;617题&#xff09; 题目要求&#xff1a; 给定两个二叉树&#xff0c;想象当你将它们中的一个覆盖到另一个上时&#xff0c;两个二叉树的一些节点便会重叠。 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠&#xff0c;那么将他们…

红日靶场5

目录 前言 外网渗透 信息收集 1、arp探测 2、nmap 3、nikto 4、whatweb 5、gobuster 6、dirsearch 漏洞探测 ThinKPHP漏洞 漏洞利用 get shell 内网渗透 信息收集 CS 启动&#xff01; CS连接 CS信息收集 1、hashdump 2、猕猴桃 3、端口扫描 MSF启动&…

解决“百度网盘启动缓慢”问题

最近在使用百度网盘&#xff0c;双击桌面的《百度网盘》图标&#xff0c;发现有等好几分钟&#xff0c;软件才会启动。百度网盘启动太慢了&#xff0c;后面发现百度网盘&#xff0c;使用dll注入技术&#xff0c;附加到很多不相干的进程里&#xff0c;比如附加explorer进程、附加…

构建基于RHEL8系列(CentOS8,AlmaLinux8,RockyLinux8等)的支持63个常见模块的PHP8.1.20的RPM包

本文适用&#xff1a;rhel8系列&#xff0c;或同类系统(CentOS8,AlmaLinux8,RockyLinux8等) 文档形成时期&#xff1a;2023年 因系统版本不同&#xff0c;构建部署应略有差异&#xff0c;但本文未做细分&#xff0c;对稍有经验者应不存在明显障碍。 因软件世界之复杂和个人能力…

Spark原理——逻辑执行图

逻辑执行图 明确逻辑计划的边界 在 Action 调用之前&#xff0c;会生成一系列的RDD,这些RDD之间的关系&#xff0c;其实就是整个逻辑计划 val conf new SparkConf().setMaster("local[6]").setAppName("wordCount_source") val sc new SparkContext(conf)v…

vue前端开发自学练习,Props数据传递-类型校验,默认值的设置!

vue前端开发自学练习,Props数据传递-类型校验,默认值的设置&#xff01; 实际上&#xff0c;vue开发框架的时候&#xff0c;充分考虑到了前端开发人员可能会遇到的各种各样的情况&#xff0c;比如大家经常遇到的&#xff0c;数据类型的校验&#xff0c;再比如&#xff0c;默认…

Web安全测试基础

SQL注入 当下最常用的一个攻击手段&#xff0c;就是通过SQL命令插入到Web表单中或页面请求查询字符串中&#xff0c;最终达到欺骗服务器执行恶意的SQL语句的目的&#xff0c;SQL注入一旦成功&#xff0c;轻则直接绕开服务器验证&#xff0c;直接登录成功&#xff0c;重则将服务…

php多小区智慧物业管理系统源码带文字安装教程

多小区智慧物业管理系统源码带文字安装教程 运行环境 服务器宝塔面板 PHP 7.0 Mysql 5.5及以上版本 Linux Centos7以上 统计分析以小区为单位&#xff0c;统计如下数据&#xff1a;小区总栋数、小区总户数、小区总人数、 小区租户数量、小区每月收费金额统计、小区车位统计、小…

专业130+总400+哈尔滨工业大学803信号与系统和数字逻辑电路考研经验哈工大,电子信息,信息与通信工程,信通

今年专业课803信号与系统和数字逻辑130总分400如愿考上哈尔滨工业大学电子信息&#xff08;信息与通信工程-信通&#xff09;&#xff0c;总结了一些各门课程复习心得&#xff0c;希望对大家复习有帮助。 数学一 资料选择&#xff1a; ①高数&#xff1a;张宇强化班 ②线性…