# ScrollRect **Repository Path**: YT_Unity/scrollrect ## Basic Information - **Project Name**: ScrollRect - **Description**: UGUI ScrollRect优化效果 - **Primary Language**: C# - **License**: WTFPL - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 12 - **Created**: 2017-07-13 - **Last Updated**: 2021-09-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # UGUI各种优化效果 本文所实现的UGUI效果需求如下: - **支持不用Mask遮罩无限循环加载** - **支持ObjectPool动态加载** - **支持无限不规则子物体动态加载** - **支持拖动并点击和拖拽** - **支持拖动并拖拽** - **支持ScrollRect拖动自动吸附功能(拖动是否超过一半自动进退)** ------------------- ## 前言 > 要实现以上效果,我从网上搜索得到部分解决方案[链接](https://github.com/qiankanglai/LoopScrollRect),但不是完全满足想要的效果,就自己继续改造优化和添加想要的效果,本文最后会附带上完整Demo下载链接。 ### 效果图 - **缩放滑动展示卡牌效果** [](http://img.blog.csdn.net/20170621134630101?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGluZ3hpYW93ZWkyMDEz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) - **缩放循环展示卡牌效果** ![这里写图片描述](http://img.blog.csdn.net/20170505180756890?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGluZ3hpYW93ZWkyMDEz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) - **大量数据无卡顿动态加载,并且支持拖拽和点击** ![这里写图片描述](http://img.blog.csdn.net/20170426065226405?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGluZ3hpYW93ZWkyMDEz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) - **无限无遮罩动态加载** ![这里写图片描述](http://img.blog.csdn.net/20170426064914545?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGluZ3hpYW93ZWkyMDEz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) - **不规则子物体动态循环加载** ![这里写图片描述](http://img.blog.csdn.net/20170426065350658?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGluZ3hpYW93ZWkyMDEz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) ### 部分核心代码 - **有遮罩无卡顿加载** 思路:并没有使用UGUI的ScrollRect组件,摆放几张卡片,通过移动和缩放来实现 ``` using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; public class EnhancelScrollView : MonoBehaviour { // 缩放曲线 public AnimationCurve scaleCurve; // 位移曲线 public AnimationCurve positionCurve; // 位移系数 public float posCurveFactor = 500.0f; // y轴坐标固定值(所有的item的y坐标一致) public float yPositionValue = 46.0f; // 添加到EnhanceScrollView的目标对象 public List scrollViewItems; // 目标对象Widget脚本,用于depth排序 private List imageTargets; // 当前处于中间的item private EnhanceItem centerItem; private EnhanceItem preCenterItem; // 当前出移动中,不能进行点击切换 private bool canChangeItem = true; // 计算差值系数 public float dFactor = 0.2f; // 点击目标移动的横向目标值 private float[] moveHorizontalValues; // 对象之间的差值数组(根据差值系数算出) private float[] dHorizontalValues; // 横向变量值 public float horizontalValue = 0.0f; // 目标值 public float horizontalTargetValue = 0.1f; // 移动动画参数 private float originHorizontalValue = 0.1f; public float duration = 0.2f; private float currentDuration = 0.0f; private static EnhancelScrollView instance; public static EnhancelScrollView GetInstance() { return instance; } void Awake() { instance = this; } void Start() { if((scrollViewItems.Count % 2) == 0) { Debug.LogError("item count is invaild,please set odd count! just support odd count."); } if(moveHorizontalValues == null) moveHorizontalValues = new float[scrollViewItems.Count]; if(dHorizontalValues == null) dHorizontalValues = new float[scrollViewItems.Count]; if (imageTargets == null) imageTargets = new List(); int centerIndex = scrollViewItems.Count / 2; for (int i = 0; i < scrollViewItems.Count;i++ ) { scrollViewItems[i].scrollViewItemIndex = i; Image tempImage = scrollViewItems[i].gameObject.GetComponent(); imageTargets.Add(tempImage); dHorizontalValues[i] = dFactor * (centerIndex - i); dHorizontalValues[centerIndex] = 0.0f; moveHorizontalValues[i] = 0.5f - dHorizontalValues[i]; scrollViewItems[i].SetSelectColor(false); } centerItem = scrollViewItems[centerIndex]; canChangeItem = true; } public void UpdateEnhanceScrollView(float fValue) { for (int i = 0; i < scrollViewItems.Count; i++) { EnhanceItem itemScript = scrollViewItems[i]; float xValue = GetXPosValue(fValue, dHorizontalValues[itemScript.scrollViewItemIndex]); float scaleValue = GetScaleValue(fValue, dHorizontalValues[itemScript.scrollViewItemIndex]); itemScript.UpdateScrollViewItems(xValue, yPositionValue, scaleValue); } } void Update() { currentDuration += Time.deltaTime; if (currentDuration > duration) { // 更新完毕设置选中item的对象即可 currentDuration = duration; if (centerItem != null) centerItem.SetSelectColor(true); if (preCenterItem != null) preCenterItem.SetSelectColor(false); canChangeItem = true; } SortDepth(); float percent = currentDuration / duration; horizontalValue = Mathf.Lerp(originHorizontalValue, horizontalTargetValue, percent); UpdateEnhanceScrollView(horizontalValue); } /// /// 缩放曲线模拟当前缩放值 /// private float GetScaleValue(float sliderValue, float added) { float scaleValue = scaleCurve.Evaluate(sliderValue + added); return scaleValue; } /// /// 位置曲线模拟当前x轴位置 /// private float GetXPosValue(float sliderValue, float added) { float evaluateValue = positionCurve.Evaluate(sliderValue + added) * posCurveFactor; return evaluateValue; } public void SortDepth() { imageTargets.Sort(new CompareDepthMethod()); for (int i = 0; i < imageTargets.Count; i++) imageTargets[i].transform.SetSiblingIndex(i); } /// /// 用于层级对比接口 /// public class CompareDepthMethod : IComparer { public int Compare(Image left, Image right) { if (left.transform.localScale.x > right.transform.localScale.x) return 1; else if (left.transform.localScale.x < right.transform.localScale.x) return -1; else return 0; } } /// /// 获得当前要移动到中心的Item需要移动的factor间隔数 /// private int GetMoveCurveFactorCount(float targetXPos) { int centerIndex = scrollViewItems.Count / 2; for (int i = 0; i < scrollViewItems.Count;i++ ) { float factor = (0.5f - dFactor * (centerIndex - i)); float tempPosX = positionCurve.Evaluate(factor) * posCurveFactor; if (Mathf.Abs(targetXPos - tempPosX) < 0.01f) return Mathf.Abs(i - centerIndex); } return -1; } /// /// 设置横向轴参数,根据缩放曲线和位移曲线更新缩放和位置 /// public void SetHorizontalTargetItemIndex(int itemIndex) { if (!canChangeItem) return; EnhanceItem item = scrollViewItems[itemIndex]; if (centerItem == item) return; canChangeItem = false; preCenterItem = centerItem; centerItem = item; // 判断点击的是左侧还是右侧计算ScrollView中心需要移动的value float centerXValue = positionCurve.Evaluate(0.5f) * posCurveFactor; bool isRight = false; if (item.transform.localPosition.x > centerXValue) isRight = true; // 差值,计算横向值 int moveIndexCount = GetMoveCurveFactorCount(item.transform.localPosition.x); if (moveIndexCount == -1) { moveIndexCount = 1; } float dvalue = 0.0f; if (isRight) dvalue = -dFactor * moveIndexCount; else dvalue = dFactor * moveIndexCount; // 更改target数值,平滑移动 horizontalTargetValue += dvalue; currentDuration = 0.0f; originHorizontalValue = horizontalValue; } /// /// 向右选择角色按钮 /// public void OnBtnRightClick() { if (!canChangeItem) return; int targetIndex = centerItem.scrollViewItemIndex + 1; if (targetIndex > scrollViewItems.Count - 1) targetIndex = 0; SetHorizontalTargetItemIndex(targetIndex); } /// /// 向左选择按钮 /// public void OnBtnLeftClick() { if (!canChangeItem) return; int targetIndex = centerItem.scrollViewItemIndex - 1; if (targetIndex < 0) targetIndex = scrollViewItems.Count - 1; SetHorizontalTargetItemIndex(targetIndex); } } ``` ``` using UnityEngine; using System.Collections; using UnityEngine.UI; public class EnhanceItem : MonoBehaviour { // 在ScrollViewitem中的索引 // 定位当前的位置和缩放 public int scrollViewItemIndex = 0; public bool inRightArea = false; private Vector3 targetPos = Vector3.one; private Vector3 targetScale = Vector3.one; private Transform mTrs; private Image mImage; void Awake() { mTrs = this.transform; mImage = this.GetComponent(); } void Start() { this.gameObject.GetComponent