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

发布时间:2024年01月12日

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

文章来源:https://blog.csdn.net/dzj2021/article/details/135543665
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。