本文所实现的UGUI效果需求如下:
- 支持缩放滑动效果
- 支持动态缩放循环加载
- 支持大数据固定Item复用加载
- 支持不用Mask遮罩无限循环加载
- 支持ObjectPool动态加载
- 支持无限不规则子物体动态加载
- 支持拖动并点击和拖拽
- 支持拖动并拖拽
- 支持ScrollRect拖动自动吸附功能(拖动是否超过一半自动进退)
前言
要实现以上效果,我从网上搜索得到部分解决方案链接,但不是完全满足想要的效果,就自己继续改造优化和添加想要的效果,本文最后会附带上完整Demo下载链接。
效果图
-
缩放滑动效果
-
缩放循环展示卡牌效果
- 大量数据无卡顿动态加载,并且支持拖拽、点击和吸附功能
-
大量数据循固定Item复用
-
无限无遮罩动态加载
- 不规则子物体动态循环加载
部分核心代码
-
有遮罩无卡顿加载
思路:并没有使用UGUI的ScrollRect组件,摆放几张卡片,通过移动和缩放来实现
1 using UnityEngine; 2 using System.Collections; 3 using System.Collections.Generic; 4 using UnityEngine.UI; 5 6 public class EnhancelScrollView : MonoBehaviour 7 { 8 // 缩放曲线 9 public AnimationCurve scaleCurve; 10 // 位移曲线 11 public AnimationCurve positionCurve; 12 // 位移系数 13 public float posCurveFactor = 500.0f; 14 // y轴坐标固定值(所有的item的y坐标一致) 15 public float yPositionValue = 46.0f; 16 17 // 添加到EnhanceScrollView的目标对象 18 public List<EnhanceItem> scrollViewItems; 19 // 目标对象Widget脚本,用于depth排序 20 private List<Image> imageTargets; 21 22 // 当前处于中间的item 23 private EnhanceItem centerItem; 24 private EnhanceItem preCenterItem; 25 26 // 当前出移动中,不能进行点击切换 27 private bool canChangeItem = true; 28 29 // 计算差值系数 30 public float dFactor = 0.2f; 31 32 // 点击目标移动的横向目标值 33 private float[] moveHorizontalValues; 34 // 对象之间的差值数组(根据差值系数算出) 35 private float[] dHorizontalValues; 36 37 // 横向变量值 38 public float horizontalValue = 0.0f; 39 // 目标值 40 public float horizontalTargetValue = 0.1f; 41 42 // 移动动画参数 43 private float originHorizontalValue = 0.1f; 44 public float duration = 0.2f; 45 private float currentDuration = 0.0f; 46 47 private static EnhancelScrollView instance; 48 public static EnhancelScrollView GetInstance() 49 { 50 return instance; 51 } 52 53 void Awake() 54 { 55 instance = this; 56 } 57 58 void Start() 59 { 60 if((scrollViewItems.Count % 2) == 0) 61 { 62 Debug.LogError("item count is invaild,please set odd count! just support odd count."); 63 } 64 65 if(moveHorizontalValues == null) 66 moveHorizontalValues = new float[scrollViewItems.Count]; 67 68 if(dHorizontalValues == null) 69 dHorizontalValues = new float[scrollViewItems.Count]; 70 71 if (imageTargets == null) 72 imageTargets = new List<Image>(); 73 74 int centerIndex = scrollViewItems.Count / 2; 75 for (int i = 0; i < scrollViewItems.Count;i++ ) 76 { 77 scrollViewItems[i].scrollViewItemIndex = i; 78 Image tempImage = scrollViewItems[i].gameObject.GetComponent<Image>(); 79 imageTargets.Add(tempImage); 80 81 dHorizontalValues[i] = dFactor * (centerIndex - i); 82 83 dHorizontalValues[centerIndex] = 0.0f; 84 moveHorizontalValues[i] = 0.5f - dHorizontalValues[i]; 85 scrollViewItems[i].SetSelectColor(false); 86 } 87 88 centerItem = scrollViewItems[centerIndex]; 89 canChangeItem = true; 90 } 91 92 public void UpdateEnhanceScrollView(float fValue) 93 { 94 for (int i = 0; i < scrollViewItems.Count; i++) 95 { 96 EnhanceItem itemScript = scrollViewItems[i]; 97 float xValue = GetXPosValue(fValue, dHorizontalValues[itemScript.scrollViewItemIndex]); 98 float scaleValue = GetScaleValue(fValue, dHorizontalValues[itemScript.scrollViewItemIndex]); 99 itemScript.UpdateScrollViewItems(xValue, yPositionValue, scaleValue); 100 } 101 } 102 103 void Update() 104 { 105 currentDuration += Time.deltaTime; 106 if (currentDuration > duration) 107 { 108 // 更新完毕设置选中item的对象即可 109 currentDuration = duration; 110 if (centerItem != null) 111 centerItem.SetSelectColor(true); 112 if (preCenterItem != null) 113 preCenterItem.SetSelectColor(false); 114 canChangeItem = true; 115 } 116 117 SortDepth(); 118 float percent = currentDuration / duration; 119 horizontalValue = Mathf.Lerp(originHorizontalValue, horizontalTargetValue, percent); 120 UpdateEnhanceScrollView(horizontalValue); 121 } 122 123 /// <summary> 124 /// 缩放曲线模拟当前缩放值 125 /// </summary> 126 private float GetScaleValue(float sliderValue, float added) 127 { 128 float scaleValue = scaleCurve.Evaluate(sliderValue + added); 129 return scaleValue; 130 } 131 132 /// <summary> 133 /// 位置曲线模拟当前x轴位置 134 /// </summary> 135 private float GetXPosValue(float sliderValue, float added) 136 { 137 float evaluateValue = positionCurve.Evaluate(sliderValue + added) * posCurveFactor; 138 return evaluateValue; 139 } 140 141 public void SortDepth() 142 { 143 imageTargets.Sort(new CompareDepthMethod()); 144 for (int i = 0; i < imageTargets.Count; i++) 145 imageTargets[i].transform.SetSiblingIndex(i); 146 } 147 148 /// <summary> 149 /// 用于层级对比接口 150 /// </summary> 151 public class CompareDepthMethod : IComparer<Image> 152 { 153 public int Compare(Image left, Image right) 154 { 155 if (left.transform.localScale.x > right.transform.localScale.x) 156 return 1; 157 else if (left.transform.localScale.x < right.transform.localScale.x) 158 return -1; 159 else 160 return 0; 161 } 162 } 163 164 /// <summary> 165 /// 获得当前要移动到中心的Item需要移动的factor间隔数 166 /// </summary> 167 private int GetMoveCurveFactorCount(float targetXPos) 168 { 169 int centerIndex = scrollViewItems.Count / 2; 170 for (int i = 0; i < scrollViewItems.Count;i++ ) 171 { 172 float factor = (0.5f - dFactor * (centerIndex - i)); 173 174 float tempPosX = positionCurve.Evaluate(factor) * posCurveFactor; 175 if (Mathf.Abs(targetXPos - tempPosX) < 0.01f) 176 return Mathf.Abs(i - centerIndex); 177 } 178 return -1; 179 } 180 181 /// <summary> 182 /// 设置横向轴参数,根据缩放曲线和位移曲线更新缩放和位置 183 /// </summary> 184 public void SetHorizontalTargetItemIndex(int itemIndex) 185 { 186 if (!canChangeItem) 187 return; 188 189 EnhanceItem item = scrollViewItems[itemIndex]; 190 if (centerItem == item) 191 return; 192 193 canChangeItem = false; 194 preCenterItem = centerItem; 195 centerItem = item; 196 197 // 判断点击的是左侧还是右侧计算ScrollView中心需要移动的value 198 float centerXValue = positionCurve.Evaluate(0.5f) * posCurveFactor; 199 bool isRight = false; 200 if (item.transform.localPosition.x > centerXValue) 201 isRight = true; 202 203 // 差值,计算横向值 204 int moveIndexCount = GetMoveCurveFactorCount(item.transform.localPosition.x); 205 if (moveIndexCount == -1) 206 { 207 moveIndexCount = 1; 208 } 209 210 float dvalue = 0.0f; 211 if (isRight) 212 dvalue = -dFactor * moveIndexCount; 213 else 214 dvalue = dFactor * moveIndexCount; 215 216 // 更改target数值,平滑移动 217 horizontalTargetValue += dvalue; 218 currentDuration = 0.0f; 219 originHorizontalValue = horizontalValue; 220 } 221 222 /// <summary> 223 /// 向右选择角色按钮 224 /// </summary> 225 public void OnBtnRightClick() 226 { 227 if (!canChangeItem) 228 return; 229 int targetIndex = centerItem.scrollViewItemIndex + 1; 230 if (targetIndex > scrollViewItems.Count - 1) 231 targetIndex = 0; 232 SetHorizontalTargetItemIndex(targetIndex); 233 } 234 235 /// <summary> 236 /// 向左选择按钮 237 /// </summary> 238 public void OnBtnLeftClick() 239 { 240 if (!canChangeItem) 241 return; 242 int targetIndex = centerItem.scrollViewItemIndex - 1; 243 if (targetIndex < 0) 244 targetIndex = scrollViewItems.Count - 1; 245 SetHorizontalTargetItemIndex(targetIndex); 246 } 247 }