[Unity3D] UGUI ScrollRect效果大全

UGUI各种优化效果

本文所实现的UGUI效果需求如下: 

支持缩放滑动效果 

支持动态缩放循环加载 

支持大数据固定Item复用加载 

支持不用Mask遮罩无限循环加载 

支持ObjectPool动态加载 

支持无限不规则子物体动态加载 

支持拖动并点击和拖拽 

支持拖动并拖拽 

支持ScrollRect拖动自动吸附功能(拖动是否超过一半自动进退)

效果图

缩放滑动效果 

缩放循环展示卡牌效果

大量数据无卡顿动态加载,并且支持拖拽、点击和吸附功能

大量数据循固定Item复用 

无限无遮罩动态加载

不规则子物体动态循环加载

部分核心代码

有遮罩无卡顿加载 

思路:并没有使用UGUI的ScrollRect组件,摆放几张卡片,通过移动和缩放来实现

usingUnityEngine;usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine.UI;publicclassEnhancelScrollView : MonoBehaviour{// 缩放曲线publicAnimationCurve scaleCurve;// 位移曲线publicAnimationCurve positionCurve;// 位移系数publicfloatposCurveFactor =500.0f;// y轴坐标固定值(所有的item的y坐标一致)publicfloatyPositionValue =46.0f;// 添加到EnhanceScrollView的目标对象publicList



有遮罩无卡顿加载 

思路:协程加载,先加载屏幕显示的数量,然后返回一帧在继续加载,防止出现数量太大卡顿的现象。

while(CardsList.Count > roleInfo.Count){    DestroyImmediate(CardsList[0].gameObject);    CardsList.RemoveAt(0);}StartCoroutine(createRoleCards());privateIEnumeratorcreateRoleCards(){    List charInfos =newList();    charInfos.AddRange(roleInfo);intindex =0;for(inti =0; i < charInfos.Count; i++)    {        _createRoleCard(charInfos[i], index++);if(index %10==0)yieldreturnnull;    }}privatevoid_createRoleCard(CLocalCharInfo roleInfo,intindex){    CUIPlayedCharCardWidget charCardWidget =null;if(CardsList.Count > index)    {        charCardWidget = CardsList[index];    }else{varobj = Instantiate(Resources.Load("Prefab/RoleCard"))asGameObject;if(obj ==null)        {            UnityEngine.Debug.LogError("有误");return;        }        obj.name = roleInfo.Name;        charCardWidget = obj.GetComponent();if(charCardWidget ==null)        {            UnityEngine.Debug.LogError("有误");return;        }        obj.transform.parent = Obj_ScrollViewContent.transform;        obj.transform.localScale = Vector3.one;        CardsList.Add(charCardWidget);    }    CUIPlayedCharCardWidget.CUIContent uiContent =newCUIPlayedCharCardWidget.CUIContent();    uiContent.RoleInfo = roleInfo;    uiContent.ScrollRectObj = m_ScrollRect;    uiContent.FixGridRect = m_FixGrid;    charCardWidget.InitContent(uiContent);}


支持ScrollRect拖拽或点击 

思路:在卡片的Image上添加一个继承了IBeginDragHandler,IGradHandler,IEndDragHandler的脚本,重写接口里面的Drag事件方法。

usingUnityEngine;usingUnityEngine.EventSystems;usingUnityEngine.UI;namespace Mga{    [RequireComponent(typeof(Image))]publicclassCPlayedCardOnDrag : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler    {publicbooldragOnSurfaces =true;publicScrollRect m_ScrollRect =null;publicCFixGridRect m_FixGridRect =null;privateGameObject m_DraggingCard;privateRectTransform m_DraggingPlane;publicboolisVertical =false;privateboolisSelf =false;privateSystem.Action m_OnBeginDragCallBack =null;privateSystem.Action m_OnEndDragCallBack =null;privateSystem.Action m_OnBeginScroll =null;privateSystem.Action m_OnEndScroll =null;publicvoidInit(CLocalCharInfo roleInfo, System.Action beginCallBack =null, System.Action endCallBack =null, System.Action beginScroll =null, System.Action endScroll =null)        {            m_OnBeginDragCallBack = beginCallBack;            m_OnEndDragCallBack = endCallBack;            m_OnBeginScroll = beginScroll;            m_OnEndScroll = endScroll;        }publicvoidOnBeginDrag(PointerEventData eventData)        {            Vector2 touchDeltaPosition = Vector2.zero;#ifUNITY_EDITORfloatdelta_x = Input.GetAxis("Mouse X");floatdelta_y = Input.GetAxis("Mouse Y");            touchDeltaPosition =newVector2(delta_x, delta_y);#elifUNITY_ANDROID || UNITY_IPHONEtouchDeltaPosition = Input.GetTouch(0).deltaPosition;#endifif(isVertical)            {if(Mathf.Abs(touchDeltaPosition.x) > Mathf.Abs(touchDeltaPosition.y))                {                    isSelf =true;varcanvas = FindInParents


如果想要实现拖拽到目标位置的检测,还要在目标位置放一个Image并且添加上继承了IDropHandler,IPointerEnterHandler,IPointerExitHanler的组件。



在ScrollRect物体上添加吸附功能组件,工程里面要使用DoTween插件

usingSystem.Collections.Generic;usingDG.Tweening;usingUnityEngine;usingUnityEngine.EventSystems;usingUnityEngine.UI;//TODO:当前只试应横向的ScrollRect,还需要扩展支持纵向publicclassCFixGridRect : MonoBehaviour, IEndDragHandler{publicGameObject content;publicScrollRect scorllRect;publicfloatitemWidth;privateRectTransform contentRectTf;privatefloatformalPosX =0;privatefloatcurrentPosX =0;privatefloathalfItemLength =0;voidStart()    {if(itemWidth <=0)            UnityEngine.Debug.LogError("请设置Item的宽度");        halfItemLength = itemWidth /2;this.contentRectTf =this.content.GetComponent();    }publicvoidOnEndDrag(PointerEventData eventData)    {this.scorllRect.StopMovement();        Vector2 afterDragPagePos =this.content.transform.localPosition;        currentPosX = afterDragPagePos.x;//当前拖动的位置  负if(scorllRect.horizontalNormalizedPosition <0|| scorllRect.horizontalNormalizedPosition >1)return;intcount = (int)(Mathf.Abs(currentPosX) / itemWidth);vartargetPos = -(float)(count * itemWidth);if(((float)(count * itemWidth + halfItemLength)) < Mathf.Abs(currentPosX))        {            targetPos = -(float)((count +1) * itemWidth);        }        formalPosX = targetPos;this.contentRectTf.DOLocalMoveX(targetPos,.2f);    }}



优化支持横竖屏的ScrollRect吸附功能

usingSystem.Collections.Generic;usingDG.Tweening;usingUnityEngine;usingUnityEngine.EventSystems;usingUnityEngine.UI;namespace Mga{publicenumDragDirection    {        Horizontal,        Vertical,    }publicclassCFixGridRectBase : MonoBehaviour, IEndDragHandler    {publicclassCUIContent        {publicGameObject ScrollRectContent;publicScrollRect m_ScorllRect;publicfloatItemSize;publicfloatItemSpaceLength;//间隙publicfloatMargin =0;//顶部边缘间隙publicDragDirection m_DragDirection = DragDirection.Vertical;        }privateRectTransform contentRectTf;privatefloathalfItemLength =0;privateCUIContent m_uiContent =null;privateboolm_bWidgetReady =false;voidStart()        {            m_bWidgetReady =true;            _initContent();        }publicvoidInitContent(CUIContent uiContent)        {            m_uiContent = uiContent;if(m_bWidgetReady)                _initContent();        }privatevoid_initContent()        {if(m_uiContent ==null)return;if(m_uiContent.ItemSize <=0)            {                UnityEngine.Debug.LogError("请设置Item的宽度");return;            }            halfItemLength = m_uiContent.ItemSize /2;this.contentRectTf = m_uiContent.ScrollRectContent.GetComponent();        }publicvoidOnEndDrag(PointerEventData eventData)        {            m_uiContent.m_ScorllRect.StopMovement();            Vector2 afterDragPagePos = m_uiContent.ScrollRectContent.transform.localPosition;varitemLength = m_uiContent.ItemSize + m_uiContent.ItemSpaceLength;if(m_uiContent.m_DragDirection == DragDirection.Horizontal)            {varcurrentPosX = afterDragPagePos.x;//当前拖动的位置  负currentPosX -= m_uiContent.Margin;intcount = (int)(Mathf.Abs(currentPosX) / m_uiContent.ItemSize);if(m_uiContent.m_ScorllRect.horizontalNormalizedPosition <=0)                {return;                }elseif(m_uiContent.m_ScorllRect.horizontalNormalizedPosition >=1)//总数-当前显示的数量{return;                }vartargetPosX = -(float)(count * itemLength);if(((float)(targetPosX + halfItemLength)) < Mathf.Abs(currentPosX))                {                    count++;                    targetPosX = -(float)(count * itemLength);                }this.contentRectTf.DOLocalMoveX(targetPosX,.2f);            }else{varcurrentPosY = afterDragPagePos.y;//当前拖动的位置  正currentPosY -= m_uiContent.Margin;intcount = (int)(Mathf.Abs(currentPosY) / itemLength);if(m_uiContent.m_ScorllRect.verticalNormalizedPosition <=0)                {return;                }elseif(m_uiContent.m_ScorllRect.verticalNormalizedPosition >=1)//总数-当前显示的数量{return;                }vartargetPosY = (float)(count * itemLength);if(((float)(targetPosY + halfItemLength)) < Mathf.Abs(currentPosY))                {                    count++;                    targetPosY = (float)(count * itemLength);                }this.contentRectTf.DOLocalMoveY(targetPosY,.2f);            }        }    }}


补充

如果代码创建AnimationCurve默认是曲线,如果想要直线效果,可以在面板里面设置,也可以代码设置,如果代码设置如下:

var curve2 = new AnimationCurve();var key1 = new Keyframe(0,0);key1.outTangent=1;var key2 = new Keyframe(1,1);key2.inTangent=1;curve2.AddKey(key1);curve2.AddKey(key2);curve2.postWrapMode= WrapMode.Loop;curve2.preWrapMode= WrapMode.Loop;


这样的话就是直线了。

推荐阅读更多精彩内容