Unity RectTransform Scale Handler - 如何在Runtime运行时拖动缩放窗口尺寸


简介

本文介绍如何在Runtime运行时拖动缩放UI窗口的尺寸,如图所示,在示例窗口的左上、上方、右上、左方、右方、左下、下方、右下,分别放置了一个拖动柄,按下进行拖动时,将改变窗口的尺寸:

示例效果

该工具源码已上传SKFramework框架Package Manager中:

SKFramework PackageManager

变量说明

RectTransform Scale Handler

  • Target:目标,即拖动该拖动柄时要改变尺寸的RectTransform;
  • Min Size Limit:最小尺寸限制值;
  • Max Size Limit:最大尺寸限制值;
  • Handler Anchor:拖动柄的锚点位置:
    • UpperLeft:左上;
    • UpperCenter:上方;
    • UpperRight:右上;
    • MiddleLeft:左方;
    • MiddleRight:右方;
    • LowerLeft:左下;
    • LowerCenter:下方;
    • LowerRight:右下;
  • Expand Mode:扩展模式,包含两种类型:
    • Whole:拖动该拖动柄时,窗口会整个(居中)进行扩展缩放;
    • Along Anchor:拖动该拖动柄时,窗口会沿拖动柄锚点方向进行扩展缩放。

Expand Mode:Whole

Expand Mode:Along Anchor

实现

光标移入移出

只有在光标进入拖动柄时,按住鼠标左键,窗口才根据拖动改变尺寸。继承IPointerEnterHandlerIPointerExitHandler接口,实现OnPointerEnterOnPointerEnter方法:

//光标是否进入
private bool isMouseEntered;

//光标移入
public void OnPointerEnter(PointerEventData eventData)
{
    
    
    isMouseEntered = true;
}
//光标移除
public void OnPointerExit(PointerEventData eventData)
{
    
    
    isMouseEntered = false;
}

鼠标拖动距离

光标进入并按下鼠标左键时,标识正在拖动,并缓存按下时鼠标的坐标位置,在拖动过程中,通过当前的鼠标坐标位置减去按下时缓存的鼠标坐标位置,就可以得到拖动产生的偏差,即距离:

//是否正在拖动
private bool isDragging;
//缓存鼠标坐标
private Vector3 cacheMousePosition;
//拖动的偏差
private Vector3 dragDelta;

private void Update()
{
    
    
    //光标已进入
    if (isMouseEntered)
    {
    
    
        //鼠标左键按下
        if (Input.GetMouseButtonDown(0))
        {
    
    
            //正在拖动标识置为true
            isDragging = true;
            //缓存鼠标位置
            cacheMousePosition = Input.mousePosition;
        }
    }
    //正在拖动
    if (isDragging)
    {
    
    
        //拖动的偏差
        dragDelta = Input.mousePosition - cacheMousePosition;
        //TODO:
    }
    //鼠标左键抬起
    if (Input.GetMouseButtonUp(0))
    {
    
    
        //恢复正在拖动标识
        isDragging = false;
        //重置拖动偏差
        dragDelta = Vector3.zero;
    }
}

Anchor 锚点

初始化时根据Handler Anchor来设置拖动柄的RectTransform组件的Anchor MinAnchor Max,并将其Anchor Position锚点坐标归零,使其定位到指定的位置:

private void Start()
{
    
    
    switch (handlerAnchor)
    {
    
    
        case Anchor.UpperLeft: rt.anchorMin = rt.anchorMax = new Vector2(0f, 1f); break;
        case Anchor.UpperCenter: rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 1f); break;
        case Anchor.UpperRight: rt.anchorMin = rt.anchorMax = new Vector2(1f, 1f); break;
        case Anchor.MiddleLeft: rt.anchorMin = rt.anchorMax = new Vector2(0f, 0.5f); break;
        case Anchor.MiddleRight: rt.anchorMin = rt.anchorMax = new Vector2(1f, 0.5f); break;
        case Anchor.LowerLeft: rt.anchorMin = rt.anchorMax = new Vector2(0f, 0f); break;
        case Anchor.LowerCenter: rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 0f); break;
        case Anchor.LowerRight: rt.anchorMin = rt.anchorMax = new Vector2(1f, 0f); break;
    }
    rt.anchoredPosition = Vector2.zero;
}

目标尺寸

如何计算目标尺寸?需要在鼠标按下时,缓存窗口的尺寸,在拖动过程中,根据按下时缓存的窗口尺寸和拖动产生的偏差来计算目标尺寸。另外,拖动导致窗口尺寸变化的影响元素还包括:

  • Handler Anchor,当其类型为UpperCenter和LowerCenter时,拖动只改变窗口的高度;当其类型为MiddleLeft和MiddleRight时,拖动只改变窗口的宽度;为其他类型时,同时改变窗口宽和高,当然方向不同决定缓存窗口尺寸是加上还是减去拖动产生的偏差;
  • Expand Mode,当扩展类型为Whole,即窗口整体进行缩放时,缩放的增量是拖动产生偏差的2倍,即系数为2;
  • Min、Max Size Limit:在最终赋值之前会通过最大最小限制值进行钳制:
//正在拖动
if (isDragging)
{
    
    
    //拖动的偏差
    dragDelta = Input.mousePosition - cacheMousePosition;
    //水平方向数学符号
    float horizontalSign = rt.anchorMin.x == 0f ? -1f : rt.anchorMin.x == .5f ? 0f : 1f;
    //垂直方向数学符号
    float verticalSign = rt.anchorMin.y == 0f ? -1f : rt.anchorMin.y == .5f ? 0f : 1f;
    //系数 扩展模式为整个时系数=2 沿锚点扩展时系数=1
    float factor = expandMode == ExpandMode.AlongAnchor ? 1f : 2f;
    //目标宽高
    float width = cacheTargetSize.x + dragDelta.x * horizontalSign * factor;
    float height = cacheTargetSize.y + dragDelta.y * verticalSign * factor;
    //最大最小值限制
    width = Mathf.Clamp(width, minSizeLimit.x, maxSizeLimit.x);
    height = Mathf.Clamp(height, minSizeLimit.y, maxSizeLimit.y);
    //设置宽高
    target.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, width);
    target.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);
}

扩展方向

当扩展模式为Along Anchor时,窗口沿锚点方向进行缩放,例如锚点为右下时,窗口轴心点为左上,窗口会往右下方向进行缩放,如图所示:

Expand Along Anchor Direction

因此,在鼠标按下时,需要根据锚点的位置,反方向设置窗口的Pivot轴心点,在此需要注意一个问题,因为锚点坐标是不变的,因此更改轴心点会导致窗口的位置发生变化,因此在设置轴心点之前先进行缓存,然后在设置之后,根据轴心点的偏差修正窗口的位置:

//光标已进入
if (isMouseEntered)
{
    
    
    //鼠标左键按下
    if (Input.GetMouseButtonDown(0))
    {
    
    
        //正在拖动标识置为true
        isDragging = true;
        //缓存鼠标位置
        cacheMousePosition = Input.mousePosition;
        //缓存目标的大小
        cacheTargetSize.x = target.rect.width;
        cacheTargetSize.y = target.rect.height;
        //缓存轴心点
        cachePivot = target.pivot;
        //根据扩展模式修改目标的锚点
        if (expandMode == ExpandMode.Whole)
            target.pivot = Vector2.one * .5f;
        else
        {
    
    
            //根据处理柄锚点位置反方位设置目标的锚点
            switch (handlerAnchor)
            {
    
    
                case Anchor.UpperLeft: target.pivot = new Vector2(1f, 0f); break;
                case Anchor.UpperCenter: target.pivot = new Vector2(0.5f, 0f); break;
                case Anchor.UpperRight: target.pivot = new Vector2(0f, 0f); break;
                case Anchor.MiddleLeft: target.pivot = new Vector2(1f, 0.5f); break;
                case Anchor.MiddleRight: target.pivot = new Vector2(0f, 0.5f); break;
                case Anchor.LowerLeft: target.pivot = new Vector2(1f, 1f); break;
                case Anchor.LowerCenter: target.pivot = new Vector2(0.5f, 1f); break;
                case Anchor.LowerRight: target.pivot = new Vector2(0f, 1f); break;
            }
        }
        //获取轴心点的偏差 
        Vector2 pivotDelta = target.pivot - cachePivot;
        //由于锚点坐标不变 更改轴心点会导致位置变更 通过偏差来修正位置
        target.anchoredPosition += new Vector2(pivotDelta.x * target.rect.width, pivotDelta.y * target.rect.height);
    }
}

鼠标抬起结束拖动时,根据缓存恢复窗口的轴心点:

//鼠标左键抬起
if (Input.GetMouseButtonUp(0))
{
    
    
    //恢复正在拖动标识
    isDragging = false;
    //重置拖动偏差
    dragDelta = Vector3.zero;
    //鼠标抬起后 根据缓存恢复目标的轴心点
    Vector2 pivot = target.pivot;
    target.pivot = cachePivot;
    Vector2 pivotDelta = target.pivot - pivot;
    target.anchoredPosition += new Vector2(pivotDelta.x * target.rect.width, pivotDelta.y * target.rect.height);
}

猜你喜欢

转载自blog.csdn.net/qq_42139931/article/details/129328497