作用
UI在指定区域内移动,不超出指定区域的边界。
要求UI比区域小。
思路
- 获取移动UI四个角的位置;将位置转换为相对于移动区域的位置;
RectTransform.GetWorldCorners获取四个角的位置
利用矩阵将点转换到相对于移动区域的位置 - 创建移动区域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;
}
}