UI在指定区域内拖拽

作用

UI在指定区域内移动,不超出指定区域的边界。

要求UI比区域小。

思路

  1. 获取移动UI四个角的位置;将位置转换为相对于移动区域的位置;
    RectTransform.GetWorldCorners获取四个角的位置
    利用矩阵将点转换到相对于移动区域的位置
  2. 创建移动区域Bounds和移动对象Bounds,通过Bounds判断UI是否超出区域
    注意:UI的宽度或高度大于移动区域,拖拽效果不理想。

脚本

脚本挂载到可以触发IDragHandler, IBeginDragHandler, IEndDragHandler接口的对象上。

设置移动区域,移动内容。

csharp 复制代码
using UnityEngine;
using UnityEngine.EventSystems;

public class SimpleDrag : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
{
    [Header("移动区域")]
    [SerializeField] RectTransform moveAreaRect;

    [Header("移动内容")]
    [SerializeField] RectTransform contentRect;

    Vector2 startLocalCursor;
    Vector2 contentStartPosition;
    bool isDrag = false;

    Bounds moveAreaBounds;
    Bounds contentBounds;

    public void OnBeginDrag(PointerEventData eventData)
    {
        if (eventData.button != PointerEventData.InputButton.Left)
            return;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(moveAreaRect,
        eventData.position, eventData.pressEventCamera,
        out startLocalCursor);
        contentStartPosition = contentRect.anchoredPosition;
        isDrag = true;
    }

    public void OnDrag(PointerEventData eventData)
    {
        if (isDrag == false) return;

        if (eventData.button != PointerEventData.InputButton.Left)
            return;

        Vector2 localCursor;
        if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(moveAreaRect,
        eventData.position, eventData.pressEventCamera, out localCursor))
            return;
        //从拖拽开始到当前帧,鼠标在 viewRect 局部坐标系中的位移
        var pointerDelta = localCursor - startLocalCursor;
        //目标位置
        var position = contentStartPosition + pointerDelta;
        //位移量
        var delta = position - contentRect.anchoredPosition;

        //区域限制
        UpdateBounds();
        var offset = CalculateOffest(delta);
        contentRect.anchoredPosition = position + offset;
    }

    private readonly Vector3[] corners = new Vector3[4];
    private void UpdateBounds()
    {
        moveAreaBounds = new Bounds(moveAreaRect.rect.center, moveAreaRect.rect.size);
        var viewWorldToLocal = moveAreaRect.worldToLocalMatrix;
        contentRect.GetWorldCorners(corners);
        var min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
        var max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
        for (int i = 0; i < corners.Length; i++)
        {
            var v = viewWorldToLocal.MultiplyPoint3x4(corners[i]);
            min = Vector3.Min(v, min);
            max = Vector3.Max(v, max);
        }
        contentBounds = new Bounds
        {
            min = min,
            max = max
        };
    }

    private Vector2 CalculateOffest(Vector2 delta)
    {
        Vector2 offset = Vector2.zero;

        Vector2 minMoveAreaValue = moveAreaBounds.min;
        Vector2 maxMoveAreaValue = moveAreaBounds.max;

        Vector2 minContentValue = contentBounds.min;
        Vector2 maxContentValue = contentBounds.max;

        minContentValue += delta;
        maxContentValue += delta;

        Vector2 minOffest = minMoveAreaValue - minContentValue;
        Vector2 maxOffest = maxMoveAreaValue - maxContentValue;

        if (minOffest.x > 0)
        {
            offset.x = minOffest.x;
        }
        if (maxOffest.x < 0)
        {
            offset.x = maxOffest.x;
        }
        if (minOffest.y > 0)
        {
            offset.y = minOffest.y;
        }
        if (maxOffest.y < 0)
        {
            offset.y = maxOffest.y;
        }
        return offset;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        if (eventData.button != PointerEventData.InputButton.Left)
            return;
        isDrag = false;
    }
}
相关推荐
JaneConan8 小时前
鸿蒙 ArkUI 深水区:@Watch 和 @Observed,状态变了「自动跑」+ 嵌套对象「深层重绘」
开发语言·后端·ui·harmonyos
施棠海10 小时前
Pixel2XML实战:10分钟从设计稿做出一个能拨号的Android拨号盘页面(附完整业务代码)
android·java·ui
施棠海12 小时前
设计稿一键变成可运行的Android页面:Pixel2XML全流程UI开发框架实战(附完整源码)
android·ui·架构
Helen_cai13 小时前
OpenHarmony 通用状态加载、空页面、错误兜底组件封装(全局业务通用 UI 组件 API23)
开发语言·ui·华为·php·harmonyos
YM52e14 小时前
鸿蒙 Flutter BoxDecoration装饰:打造精美UI效果
学习·flutter·ui·华为·harmonyos·鸿蒙
qq_4634084214 小时前
# 基于 HarmonyOS ArkTS 声明式 UI 构建智能音乐播放器:深色主题圆盘轮播、五 Tab 导航隔离、瀑布流收藏与悬浮迷你播放条实现深度解析
ui·华为·harmonyos
工业HMI实战笔记1 天前
【无标题】
大数据·人工智能·ui·自动化·人机交互·交互
帅次1 天前
Kotlin Flow 与 StateFlow:UI 单向数据流基础
开发语言·ui·kotlin·stateflow·onlifecycle
一个处女座的程序猿2 天前
Agent之Skill:ui-ux-pro-max-skill的简介、安装和使用方法、案例应用之详细攻略
ui·agent·ux·ui-ux-skill