UGUI:CenterOnChild
参照NGUI写的一个UGUI的CenterOnChild。
主要思路是监听ScrollRect的开始滑动和结束滑动。在结束滑动的的时候遍历一遍Grid下的Item.找出离中心点最近的那个Item,然后计算一个把这个Item拉到中心点的Grid的偏移量。接着把Grid滑动到这个位置就行了。
需要注意的一个地方是:ScrollRect组件竟然没有滑动开始和结束的回调。不过好在UGUI的事件系统可以监听手指开始滑动和停止滑动的事件。继承IEndDragHandler和IBeginDragHandler接口,并把脚本挂在ScrollRect下就能监听滑动开始和结束了。所以与NGUI不同的是这个CenterOnChild必须要挂在ScrollRect下,而不是Grid.
除了与NGUI一样的CenterOnChild,我还加了一个CenterOnItem的模式,这个发模式下Item不是居中而是相对于起始位置居中。让距离ScrollRect的起始滑动点最近的Item总能刚好处在ScrollRect边框内。大概就是下面这个样子:
不过用这个CenterOnChild的时候最好选择ScrollRect的滑动模式为Unrestricted.不然滑动会受ScrollRect的限制,到时两段的Item可能不能显示全的情况。
usingSystem.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;
public class CenterOnChild : MonoBehaviour, IEndDragHandler, IBeginDragHandler
{
public Action onCenterCallBack;
public Action onCenterFinshed;
public GameObject centeredObject { get; private set; }
public CenterMode centerMode = CenterMode.centerOnChild;
public float speed = 10;
ScrollRect scroll;
GridLayoutGroup grid;
Transform content;
void Awake()
{
scroll = GetComponentInParent();
content = scroll.content;
grid = content.GetComponent();
}
void Start()
{
//考虑到Grid排列会先打乱Item的坐标,所以等排列完成再居中
Invoke("reCenter",0.05f);
}
Vector3 CalPickingPoint()
{
Vector3 pickingPoint = Vector3.zero;
//裁剪区域四角坐标
Vector2 halfSize = (scroll.transform as RectTransform).rect.size / 2;
Vector2[] Conners = new Vector2[4];
Conners[0] = new Vector2(-halfSize.x, halfSize.y);
Conners[1] = new Vector2(halfSize.x, halfSize.y);
Conners[2] = new Vector2(halfSize.x, -halfSize.y);
Conners[3] = new Vector2(-halfSize.x, -halfSize.y);
if (centerMode == CenterMode.centerOnChild)
{
for (int i = 0; i < 4; i++)
Conners[i] = scroll.transform.TransformPoint(Conners[i]);
pickingPoint = (Conners[0] + Conners[2]) *0.5f;
}
else
{
RectTransform item = content.GetChild(0)as RectTransform;
Vector2 half_itemSize= item.rect.size*0.5f;
half_itemSize.y *= -1;
pickingPoint =scroll.transform.TransformPoint( Conners[0] + half_itemSize);
Debug.Log(pickingPoint);
}
return pickingPoint;
}
public void reCenter()
{
Vector3 pickingPoint = CalPickingPoint();
float minDist = float.MaxValue;
Transform closest = null;
for (int i = 0, Imax = content.childCount; i < Imax; i++)
{
Transform child = content.GetChild(i);
if (!child.gameObject.activeInHierarchy) continue;
float dist = Vector2.SqrMagnitude(child.position - pickingPoint);
if (dist < minDist)
{
minDist = dist;
closest = child;
}
}
CenterOn(closest, pickingPoint);
}
void CenterOn(Transform target, Vector3 centerPos)
{
if (target != null && scroll != null)
{
centeredObject = target.gameObject;
Vector3 cp = content.parent.InverseTransformPoint(target.position);
Vector3 cc = content.parent.InverseTransformPoint(centerPos);
Vector3 localOffset = cc - cp;
if (!scroll.horizontal) localOffset.x = 0;
if (!scroll.vertical) localOffset.y = 0;
localOffset.z = 0;
targetPos = content.localPosition + localOffset;
centering = true;
if (onCenterCallBack != null) onCenterCallBack(target.gameObject);
}
}
public void CenterOn(Transform target)
{
CenterOn(target, CalPickingPoint());
}
Vector3 targetPos;
bool centering = false;
void Update()
{
if(centering)
{
Vector2 v = content.localPosition;
content.localPosition = Vector2.Lerp(content.localPosition, targetPos, speed * Time.deltaTime);
if (Vector2.Distance(content.localPosition,targetPos) < 0.01f)
{
content.localPosition = targetPos;
centering = false;
if (onCenterFinshed != null) onCenterFinshed(centeredObject);
}
}
}
public void OnEndDrag(PointerEventData eventData)
{
scroll.StopMovement();
reCenter();
}
public void OnBeginDrag(PointerEventData eventData)
{
centering = false;
targetPos = Vector3.zero;
}
public enum CenterMode
{
centerOnChild,
centerOnItem,
}
}
- 上一篇: 用栈来管理UI界面:SceneLoadManager
- 下一篇: UGUI:一个渐变特写的滚动框特效