Unity UGUI 效果 之 鼠标拖拽UI边缘,简单实现边缘实时调整UI大小 Resize 的功能

 

 

Unity UGUI 效果 之 鼠标拖拽UI边缘,简单实现边缘实时调整UI大小 Resize 的功能

 

目录

Unity UGUI 效果 之 鼠标拖拽UI边缘,简单实现边缘实时调整UI大小 Resize 的功能

一、简单介绍

二、实现原理

三、注意事项

四、效果预览

五、实现步骤

六、关键代码


 

一、简单介绍

UGUI,是Unity自带的 GUI 系统,有别于 NGUI;使用 UGUI 也能制作出比较酷炫的效果 。

本节介绍,使用 UGUI 通过代码实现鼠标在UI的边缘,点击拖拽UI实现动态调整图片大小的功能,方法不唯一,欢迎指正。

 

二、实现原理

1、OnPointerDown 鼠标在UI 上点击,记录变化前的大小和鼠标位置

2、OnDrag 获得鼠标拖拽的位置与之前点击鼠标位置的差值,实现大小的变化

3、OnPointerEnter、OnPointerExit、OnPointerUp 判断鼠标所处边缘的位置

 

三、注意事项

1、鼠标的点击位置区分左上和右下,实现,鼠标向内缩小,向外放大的效果,不然变化会很怪异

2、注意 UI 锚点都在中心位置

3、边缘变化这里变化是 UI 的比例,可以根据也可以自行改为变化 UI 的 宽高

4、由于变化 UI 比例,边缘图标随鼠标移动可能会有位置不对应的情况,建议可以不移动 边缘图标,或者改变移动的比例哈

5、可能需要注意 UI 锚点的设置

 

四、效果预览

 

五、实现步骤

1、打开Unity,新建一个工程

 

2、添加一个 Image,作为调整大小的 UI ,其他布局如图所示

 

3、其中 EdgeImages 平铺 Image,各个边缘图标的锚点在中央,如图

4、这里UI Image 的锚点都在中心位置

 

5、在工程中新建脚本,实现点击UI,拖拽UI 边缘 实现调整UI大小的功能

 

6、脚本挂载到 UI 上,可以根据使用情况调整限定最大最小值,并对应赋值

 

7、运行场景,效果如上

 

六、关键代码

1、ResizePanel

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;

namespace XANTools
{
    /// <summary>
    /// 拖动边缘,调整UI大小
    /// 使用说明
    /// 1、鼠标移动到UI的边缘,点击鼠标向内或向外拖动,实现调整UI大小
    /// 2、这里变化的 UI 的比例,你可以根据需要需改为 UI 宽高
    /// </summary>
    public class ResizePanel : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerEnterHandler, IPointerExitHandler, IPointerUpHandler
    {
        /// <summary>
        /// 边缘枚举
        /// </summary>
        public enum UIEdge
        {
            None,
            Left,
            Right,
            Down
        }

        /// <summary>
        /// 画布变化的大小限制值
        /// </summary>
        [Header("Resize 变化大小限制值")]
        public Vector3 minSize = new Vector3(0.5f, 0.5f, 0.5f);
        public Vector3 maxSize = new Vector3(3.0f, 3.0f, 3.0f);

        [Header("边缘判断使用")]
        // 画布,用于鼠标转UI位置使用
        public Canvas canvas;
        // 边缘的图片变量
        public Transform Right_Image;
        public Transform Down_Image;
        public Transform Left_Image;

        // UI RectTransform
        private RectTransform panelRectTransform;
        // UI 变化前原始鼠标位置
        private Vector2 originalLocalPointerPosition;
        // UI 变化前原始比例
        private Vector3 originalScale;
        // 左上右下的区别判断
        private bool isUpLeft = false;
        // Resize 的变化平滑值
        private float floatResizeSmooth = 30;

        // 边缘判断处的位置变量
        Vector2 v2PointEnter;

        // 鼠标是否按下
        private bool isPinterDown = false;
        // 当前鼠标大概在什么位置
        private UIEdge uiEdge = UIEdge.None;

        // 边缘位置判断距离
        private float floatEdgeDistance = 100;





        public UnityEvent OnTestEnevt;
        [SerializeField]
        private UnityEvent OnTestEnevt2;



        void Awake()
        {
            // 初始化
            panelRectTransform = transform.GetComponent<RectTransform>();

            // 隐藏边缘图标
            SetActiveEdgeImage(false);
        }

        /// <summary>
        /// 鼠标在UI上按下的事件
        /// </summary>
        /// <param name="data"></param>
        public void OnPointerDown(PointerEventData data)
        {
            // 鼠标按下
            isPinterDown = true;
            // UI 原始比例
            originalScale = this.transform.localScale;

            // 鼠标位置转为对应 UI的坐标
            RectTransformUtility.ScreenPointToLocalPointInRectangle(panelRectTransform, data.position, data.pressEventCamera, out originalLocalPointerPosition);

            // 左上右下的区别判断
            if ((originalLocalPointerPosition.x - originalLocalPointerPosition.y) < 0)
            {
                isUpLeft = true;
            }
            else
            {
                isUpLeft = false;
            }
            // Debug.Log(GetType() + "/OnPointerDown()/originalLocalPointerPosition : " + originalLocalPointerPosition);
        }

        /// <summary>
        /// 拖拽实现Resize
        /// </summary>
        /// <param name="data"></param>
        public void OnDrag(PointerEventData data)
        {
            // 停止边缘判断协程
            StopAllCoroutines();
            if (uiEdge == UIEdge.None)
                return;

            if (panelRectTransform == null)
                return;

            Vector2 localPointerPosition;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(panelRectTransform, data.position, data.pressEventCamera, out localPointerPosition);
            Vector3 offsetToOriginal = localPointerPosition - originalLocalPointerPosition;
            Vector3 sizeDelta = originalScale;

            // 根据边缘不同进行不同的变化(根据需要调整)
            switch (uiEdge)
            {
                case UIEdge.None:
                    break;

                case UIEdge.Left:
                case UIEdge.Right:
                    // 仅在 X 方向变化
                    sizeDelta = originalScale + new Vector3(offsetToOriginal.x, 0, 0) * (isUpLeft == true ? -1 : 1) * 0.01f;
                    sizeDelta = new Vector3(
                            Mathf.Clamp(sizeDelta.x, minSize.x, maxSize.x),
                            Mathf.Clamp(sizeDelta.y, minSize.y, maxSize.y),
                            Mathf.Clamp(sizeDelta.z, minSize.z, maxSize.z)
                    );
                    break;
                case UIEdge.Down:
                    // 仅在 Y 上变化
                    sizeDelta = originalScale + new Vector3(0, -offsetToOriginal.y, 0) * (isUpLeft == true ? -1 : 1) * 0.01f;
                    sizeDelta = new Vector3(
                            Mathf.Clamp(sizeDelta.x, minSize.x, maxSize.x),
                            Mathf.Clamp(sizeDelta.y, minSize.y, maxSize.y),
                            Mathf.Clamp(sizeDelta.z, minSize.z, maxSize.z)
                    );
                    break;
                default:
                    break;
            }


            //Debug.Log(GetType() + "/OnDrag()/sizeDelta : " + sizeDelta);
            // 赋值产生变化
            panelRectTransform.localScale = Vector3.Lerp(panelRectTransform.localScale, sizeDelta, Time.deltaTime * floatResizeSmooth);
        }



        /// <summary>
        /// 鼠标进入UI的事件
        /// </summary>
        /// <param name="eventData"></param>
        public void OnPointerEnter(PointerEventData eventData)
        {

            //Debug.Log(" eventData.position:" + eventData.position);

            //把鼠标的的位置转为对应UI上的位置(这里可以省略,因为没有用到)
            //RectTransformUtility.ScreenPointToLocalPointInRectangle(panelRectTransform, eventData.position, eventData.pressEventCamera, out v2PointEnter);

            // 开启边缘检测
            StartCoroutine(edgeJudge());

            //Debug.Log(GetType()+ "/ OnPointerEnter() / v2PointEnter " + v2PointEnter);
            //Debug.Log(GetType()+ "/ OnPointerEnter() / panelRectTransform.rect.width " + panelRectTransform.rect.width);
            //Debug.Log(GetType()+ "/ OnPointerEnter() / panelRectTransform.rect.height " + panelRectTransform.rect.height);

        }

        /// <summary>
        /// 鼠标退出UI的事件
        /// </summary>
        /// <param name="eventData"></param>
        public void OnPointerExit(PointerEventData eventData)
        {
            //Debug.Log(GetType() + "/ OnPointerExit() / 退出 " );
            if (isPinterDown == false)
            {
                StopAllCoroutines();
                SetActiveEdgeImage(false);
                uiEdge = UIEdge.None;
            }

        }


        /// <summary>
        /// 鼠标抬起事件
        /// </summary>
        /// <param name="eventData"></param>
        public void OnPointerUp(PointerEventData eventData)
        {
            isPinterDown = false;
            StopAllCoroutines();
            SetActiveEdgeImage(false);
            uiEdge = UIEdge.None;
        }

        /// <summary>
        /// 设置边缘画的显隐
        /// </summary>
        /// <param name="isActive"></param>
        private void SetActiveEdgeImage(bool isActive)
        {
            Right_Image.gameObject.SetActive(isActive);
            Down_Image.gameObject.SetActive(isActive);
            Left_Image.gameObject.SetActive(isActive);
        }

        /// <summary>
        /// 判断鼠标在 Panel 的那个边缘
        /// </summary>
        /// <param name="pos"></param>
        /// <returns></returns>
        private UIEdge GetCurrentEdge(Vector2 pos)
        {

            if ((pos.x - (-panelRectTransform.rect.width)) < (floatEdgeDistance / panelRectTransform.localScale.x))
            {
                return UIEdge.Left;
            }
            else if ((pos.y - (-panelRectTransform.rect.height)) < (floatEdgeDistance / panelRectTransform.localScale.y))
            {
                return UIEdge.Down;
            }
            else if ((panelRectTransform.rect.width - pos.x) < (floatEdgeDistance / panelRectTransform.localScale.x))
            {
                return UIEdge.Right;
            }

            return UIEdge.None;
        }

        /// <summary>
        /// 边缘判断协程
        /// </summary>
        /// <returns></returns>
        IEnumerator edgeJudge()
        {
            yield return new WaitForEndOfFrame();
            while (true)
            {

                RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform,
                Input.mousePosition,
                canvas.worldCamera, out v2PointEnter);
                //Debug.Log("_pos:" + _pos);

                uiEdge = GetCurrentEdge(v2PointEnter);
                //Debug.Log("edge:" + uiEdge.ToString());

                SetActiveEdgeImage(false);

                switch (uiEdge)
                {
                    case UIEdge.None:
                        SetActiveEdgeImage(false);
                        break;
                    case UIEdge.Left:
                        Left_Image.gameObject.SetActive(true);
                        Left_Image.localPosition = new Vector3(Left_Image.localPosition.x, Mathf.Clamp(v2PointEnter.y, -panelRectTransform.rect.height / 2, panelRectTransform.rect.height / 2),
                            Left_Image.localPosition.z);
                        break;
                    case UIEdge.Right:
                        Right_Image.gameObject.SetActive(true);
                        Right_Image.localPosition = new Vector3(Right_Image.localPosition.x, Mathf.Clamp(v2PointEnter.y, -panelRectTransform.rect.height / 2, panelRectTransform.rect.height / 2),
                            Right_Image.localPosition.z);


                        break;
                    case UIEdge.Down:
                        Down_Image.gameObject.SetActive(true);
                        //Debug.Log("panelRectTransform.rect.width:" + panelRectTransform.rect.width);
                        Down_Image.localPosition = new Vector3(Mathf.Clamp(v2PointEnter.x, -panelRectTransform.rect.width / 2, panelRectTransform.rect.width / 2),
                            Down_Image.localPosition.y, Down_Image.localPosition.z);


                        break;
                    default:
                        SetActiveEdgeImage(false);
                        break;
                }
                yield return new WaitForEndOfFrame();
            }

        }
    }
}


 

2、根绝需要人为调整 的版本(根据需要选择使用)

(1、修正,Image 只能在原点(0,0,0)使用;2、等比例缩放;3、移动图标保持不动;等等)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;

namespace XANTools
{
    /// <summary>
    /// 拖动边缘,调整UI大小
    /// 使用说明
    /// 1、鼠标移动到UI的边缘,点击鼠标向内或向外拖动,实现调整UI大小
    /// 2、这里变化的 UI 的比例,你可以根据需要需改为 UI 宽高
    /// </summary>
    public class ResizePanel : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerEnterHandler, IPointerExitHandler, IPointerUpHandler
    {
        /// <summary>
        /// 边缘枚举
        /// </summary>
        public enum UIEdge
        {
            None,
            Left,
            Right,
            Down
        }

        /// <summary>
        /// 画布变化的大小限制值
        /// </summary>
        [Header("Resize 变化大小限制值")]
        public Vector3 minSize = new Vector3(0.5f, 0.5f, 0.5f);
        public Vector3 maxSize = new Vector3(3.0f, 3.0f, 3.0f);

        [Header("边缘判断使用")]        
        // 边缘的图片变量
        public Transform Right_Image;
        public Transform Down_Image;
        public Transform Left_Image;

        // 画布相加,用于鼠标转UI位置使用
        private Camera  canvasCamera;
        // UI RectTransform
        private RectTransform panelRectTransform;
        // UI 变化前原始鼠标位置
        private Vector2 originalLocalPointerPosition;
        // UI 变化前原始比例
        private Vector3 originalScale;
        // 左上右下的区别判断
        private bool isUpLeft = false;
        // Resize 的变化平滑值
        private float floatResizeSmooth = 30;

        // 边缘判断处的位置变量
        Vector2 v2PointEnter;

        // 鼠标是否按下
        private bool isPinterDown = false;
        // 当前鼠标大概在什么位置
        private UIEdge uiEdge = UIEdge.None;

        // 边缘位置判断距离
        private float floatEdgeDistance = 100;


        void Awake()
        {
            // 初始化
            panelRectTransform = transform.GetComponent<RectTransform>();

            // 隐藏边缘图标
            SetActiveEdgeImage(false);
        }

        /// <summary>
        /// 鼠标在UI上按下的事件
        /// </summary>
        /// <param name="data"></param>
        public void OnPointerDown(PointerEventData data)
        {
            // 鼠标按下
            isPinterDown = true;
            // UI 原始比例
            originalScale = this.transform.localScale;

            // 鼠标位置转为对应 UI的坐标
            RectTransformUtility.ScreenPointToLocalPointInRectangle(panelRectTransform, data.position, data.pressEventCamera, out originalLocalPointerPosition);

            // 左上右下的区别判断
            if ((originalLocalPointerPosition.x - originalLocalPointerPosition.y) < 0)
            {
                isUpLeft = true;
            }
            else
            {
                isUpLeft = false;
            }
            // Debug.Log(GetType() + "/OnPointerDown()/originalLocalPointerPosition : " + originalLocalPointerPosition);
        }

        /// <summary>
        /// 拖拽实现Resize
        /// </summary>
        /// <param name="data"></param>
        public void OnDrag(PointerEventData data)
        {
            // 停止边缘判断协程
            StopAllCoroutines();
            if (uiEdge == UIEdge.None)
                return;

            if (panelRectTransform == null)
                return;

            Vector2 localPointerPosition;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(panelRectTransform, data.position, data.pressEventCamera, out localPointerPosition);
            Vector3 offsetToOriginal = localPointerPosition - originalLocalPointerPosition;
            Vector3 sizeDelta = originalScale;

            // 根据边缘不同进行不同的变化(根据需要调整)
            switch (uiEdge)
            {
                case UIEdge.None:
                    break;

                case UIEdge.Left:
                case UIEdge.Right:
                    // 仅在 X 方向变化,同时 Y 上与之同样的变化
                    sizeDelta = originalScale + new Vector3(offsetToOriginal.x, offsetToOriginal.x, 0) * (isUpLeft == true ? -1 : 1) * 0.01f;
                    sizeDelta = new Vector3(
                            Mathf.Clamp(sizeDelta.x, minSize.x, maxSize.x),
                            Mathf.Clamp(sizeDelta.y, minSize.y, maxSize.y),
                            Mathf.Clamp(sizeDelta.z, minSize.z, maxSize.z)
                    );
                    break;
                case UIEdge.Down:
                    // 仅在 Y 上变化,同时 X 上与之同样的变化
                    sizeDelta = originalScale + new Vector3(-offsetToOriginal.y, -offsetToOriginal.y, 0) * (isUpLeft == true ? -1 : 1) * 0.01f;
                    sizeDelta = new Vector3(
                            Mathf.Clamp(sizeDelta.x, minSize.x, maxSize.x),
                            Mathf.Clamp(sizeDelta.y, minSize.y, maxSize.y),
                            Mathf.Clamp(sizeDelta.z, minSize.z, maxSize.z)
                    );
                    break;
                default:
                    break;
            }


            //Debug.Log(GetType() + "/OnDrag()/sizeDelta : " + sizeDelta);
            // 赋值产生变化
            panelRectTransform.localScale = Vector3.Lerp(panelRectTransform.localScale, sizeDelta, Time.deltaTime * floatResizeSmooth);
        }



        /// <summary>
        /// 鼠标进入UI的事件
        /// </summary>
        /// <param name="eventData"></param>
        public void OnPointerEnter(PointerEventData eventData)
        {
            if (canvasCamera == null) canvasCamera = eventData.enterEventCamera;
            //Debug.Log(" eventData.position:" + eventData.position);

            //把鼠标的的位置转为对应UI上的位置(这里可以省略,因为没有用到)
            //RectTransformUtility.ScreenPointToLocalPointInRectangle(panelRectTransform, eventData.position, eventData.pressEventCamera, out v2PointEnter);

            // 开启边缘检测
            StartCoroutine(edgeJudge());

            //Debug.Log(GetType()+ "/ OnPointerEnter() / v2PointEnter " + v2PointEnter);
            //Debug.Log(GetType()+ "/ OnPointerEnter() / panelRectTransform.rect.width " + panelRectTransform.rect.width);
            //Debug.Log(GetType()+ "/ OnPointerEnter() / panelRectTransform.rect.height " + panelRectTransform.rect.height);

        }

        /// <summary>
        /// 鼠标退出UI的事件
        /// </summary>
        /// <param name="eventData"></param>
        public void OnPointerExit(PointerEventData eventData)
        {
            //Debug.Log(GetType() + "/ OnPointerExit() / 退出 " );
            if (isPinterDown == false)
            {
                StopAllCoroutines();
                SetActiveEdgeImage(false);
                uiEdge = UIEdge.None;
            }

        }


        /// <summary>
        /// 鼠标抬起事件
        /// </summary>
        /// <param name="eventData"></param>
        public void OnPointerUp(PointerEventData eventData)
        {
            isPinterDown = false;
            StopAllCoroutines();
            SetActiveEdgeImage(false);
            uiEdge = UIEdge.None;
        }

        /// <summary>
        /// 设置边缘画的显隐
        /// </summary>
        /// <param name="isActive"></param>
        private void SetActiveEdgeImage(bool isActive)
        {
            Right_Image.gameObject.SetActive(isActive);
            Down_Image.gameObject.SetActive(isActive);
            Left_Image.gameObject.SetActive(isActive);
        }

        /// <summary>
        /// 判断鼠标在 Panel 的那个边缘
        /// </summary>
        /// <param name="pos"></param>
        /// <returns></returns>
        private UIEdge GetCurrentEdge(Vector2 pos)
        {

            if ((pos.x - (-panelRectTransform.rect.width)) < (floatEdgeDistance ))
            {
                return UIEdge.Left;
            }
            else if ((pos.y - (-panelRectTransform.rect.height)) < (floatEdgeDistance ))
            {
                return UIEdge.Down;
            }
            else if ((panelRectTransform.rect.width - pos.x) < (floatEdgeDistance ))
            {
                return UIEdge.Right;
            }

            return UIEdge.None;
        }

        /// <summary>
        /// 边缘判断协程
        /// </summary>
        /// <returns></returns>
        IEnumerator edgeJudge()
        {
            yield return new WaitForEndOfFrame();
            while (true)
            {

                RectTransformUtility.ScreenPointToLocalPointInRectangle(panelRectTransform.transform as RectTransform,
                Input.mousePosition,
                canvasCamera, out v2PointEnter);
                Debug.Log("_pos:" + v2PointEnter);

                uiEdge = GetCurrentEdge(v2PointEnter);
                Debug.Log("edge:" + uiEdge.ToString());

                SetActiveEdgeImage(false);

                switch (uiEdge)
                {
                    case UIEdge.None:
                        SetActiveEdgeImage(false);
                        break;
                    case UIEdge.Left:
                        Left_Image.gameObject.SetActive(true);
                        // 暂时取消边缘图标移动(移动位置随着Scale比例,会有所对应不同)
                        //Left_Image.localPosition = new Vector3(Left_Image.localPosition.x, Mathf.Clamp(v2PointEnter.y, -panelRectTransform.rect.height / 2, panelRectTransform.rect.height / 2),
                        //    Left_Image.localPosition.z);
                        break;
                    case UIEdge.Right:
                        Right_Image.gameObject.SetActive(true);
                        // 暂时取消边缘图标移动(移动位置随着Scale比例,会有所对应不同)
                        //Right_Image.localPosition = new Vector3(Right_Image.localPosition.x, Mathf.Clamp(v2PointEnter.y, -panelRectTransform.rect.height / 2, panelRectTransform.rect.height / 2),
                        //    Right_Image.localPosition.z);


                        break;
                    case UIEdge.Down:
                        Down_Image.gameObject.SetActive(true);
                        //Debug.Log("panelRectTransform.rect.width:" + panelRectTransform.rect.width);
                        // 暂时取消边缘图标移动(移动位置随着Scale比例,会有所对应不同)
                        //Down_Image.localPosition = new Vector3(Mathf.Clamp(v2PointEnter.x, -panelRectTransform.rect.width / 2, panelRectTransform.rect.width / 2),
                        //    Down_Image.localPosition.y, Down_Image.localPosition.z);


                        break;
                    default:
                        SetActiveEdgeImage(false);
                        break;
                }
                yield return new WaitForEndOfFrame();
            }

        }
    }
}


 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仙魁XAN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值