Unity RectTransform Scale Handler - 如何在Runtime运行时拖动缩放窗口尺寸
Posted CoderZ1010
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity RectTransform Scale Handler - 如何在Runtime运行时拖动缩放窗口尺寸相关的知识,希望对你有一定的参考价值。
文章目录
简介
本文介绍如何在Runtime运行时拖动缩放UI窗口的尺寸,如图所示,在示例窗口的左上、上方、右上、左方、右方、左下、下方、右下,分别放置了一个拖动柄
,按下进行拖动时,将改变窗口的尺寸:
该工具源码已上传SKFramework
框架Package Manager
中:
变量说明
Target
:目标,即拖动该拖动柄时要改变尺寸的RectTransform;Min Size Limit
:最小尺寸限制值;Max Size Limit
:最大尺寸限制值;Handler Anchor
:拖动柄的锚点位置:UpperLeft
:左上;UpperCenter
:上方;UpperRight
:右上;MiddleLeft
:左方;MiddleRight
:右方;LowerLeft
:左下;LowerCenter
:下方;LowerRight
:右下;
Expand Mode
:扩展模式,包含两种类型:Whole
:拖动该拖动柄时,窗口会整个(居中)进行扩展缩放;Along Anchor
:拖动该拖动柄时,窗口会沿拖动柄锚点方向进行扩展缩放。
实现
光标移入移出
只有在光标进入拖动柄时,按住鼠标左键,窗口才根据拖动改变尺寸。继承IPointerEnterHandler
、IPointerExitHandler
接口,实现OnPointerEnter
、OnPointerEnter
方法:
//光标是否进入
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 Min
和Anchor 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时,窗口沿锚点方向进行缩放,例如锚点为右下时,窗口轴心点为左上,窗口会往右下方向进行缩放,如图所示:
因此,在鼠标按下时,需要根据锚点的位置,反方向设置窗口的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);
Unity——RectTransform详解
参考技术A unity中的ui元素是有严格的父子关系的,子物体的位置是根据父物体的变化而变化的,而子物体和父物体联系的桥梁就是Anchor。在recttransform面板中可以调整锚点的值想要清晰的理解Recttransform的各个属性,个人认为首先需要建立的第一个概念就是 绝对布局 以及 相对布局 这两个概念。
所谓的绝对布局,就是出现 锚点 的情况,此时的recttransform面板中的属性变成了
所谓相对布局,就是出现锚框的情况。在这种情况下UI元素的四个角,距离四个对应的锚点的距离是不变的,在这种情况下RectTransform的属性又变为了 Left , Top , Right , Bottom , PosZ ,其中的 PosZ 表征的是该元素到父物体在Z轴上的偏移,利用这个值可以调整UI元素的显示顺序,不过我用的不多,这里不作太多讨论。剩下的四个值应该很好理解了,就是UI元素的每一条边距离父物体的每一条边的距离。
在示意图的情况下,我设定了红色图片(子物体)距离灰色图片(父物体)的每一条边的距离都是200个单位。
Pivot中心点,就是该UI元素旋转缩放的中心点,左下角为(0,0)右上角为(1,1)
首先说说OffsetMax,其实OffsetMin也是同理。接下来会主要解释两个问题
以前对这个属性是真的一脸懵逼,网上很多教程说这个值可以设置UI元素的大小,但是真的有时候好用,有时候有不好用,真的一头雾水,官方文档说的也是很笼统,但是现在搞清楚了其中的联系以后,就觉得清晰了不少了。
所以就会出现有时候sizeDelta得到的是UI元素的大小,有时候又不是的情况,下面就复现一下这两种情况
那么我们在锚框的情况下要怎么样才能获得元素的大小呢?这个时候就可以用到rect属性了。
rect中的属性,不与UI元素所在的位置有关,只和其自身属性相关,所以其中的 rect.width 和 rect.height 属性就可以让我们在任何情况下取得元素的大小,而 rect.x 和 rect.y 如图所示,表示的是以Pivot为原点,UI元素左下角的坐标,可以看到图中Pivot是在UI元素的正中间,所以左下角的坐标就刚好是 (-100,-100)
但是有一个问题,rect属性是一个只读的属性,如果我们想要设置UI元素的大小的话,这好像又不适用了,所以RectTransform还提供了几个非常有用的方法。
通过直接设置anchoredPosition的值可以改变UI元素的位置,但也是要分 锚点 和 锚框 的情况
这个方法无论在 绝对布局 还是 相对布局 的情况下,都可以通过直接设置rect中的 width 和 height 值来改变UI元素的大小。
这个方法就比较冷门了可能,不过还是挺强大的。调用这个方法,可以根据父物体的Edge(某一边)去布局。其中第一个参数就是用于确定基准的边,第二个参数是UI元素的该边界与父物体该边界的距离,第三个元素是设定选定轴上UI元素的大小,可能说起来有点复杂,但是我上两张图相信各位就可以秒懂了。
首先以右边界为基准
然后以下边界为基准
在使用这个方法的时候要注意锚点也会改变,改变的规则为
使用这个方法,可以取得UI元素四个角的世界坐标,具体使用方法,先建立一个长度为4的vector3数组,然后传进这个方法,调用一次后,数组被赋值,里面的四个元素分别是UI的 左下角 , 左上角 , 右上角 , 右下角 。
以上是关于Unity RectTransform Scale Handler - 如何在Runtime运行时拖动缩放窗口尺寸的主要内容,如果未能解决你的问题,请参考以下文章
关于Unity中RectTransform和Transform
Unity UGUI 修改好了RectTransform的值但是重启工程后RectTransform会自己变化,如何解决呢