ScrollPanel.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. using UnityEngine.Events;
  6. using UnityEngine.EventSystems;
  7. namespace JC
  8. {
  9. public class ScrollPanel : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerDownHandler, IPointerUpHandler
  10. {
  11. //public enum scrollAlignment
  12. //{
  13. // Upper,
  14. // Middle,
  15. // Lower
  16. //}
  17. public enum InitType {
  18. Statics, Dynamic
  19. }
  20. public InitType initType = InitType.Statics;
  21. ScrollRect _scrollRect;
  22. RectTransform _rectTransform;
  23. ScrollRect scrollRect {
  24. get {
  25. if (!_scrollRect)
  26. {
  27. _scrollRect = this.GetComponent<ScrollRect>();
  28. }
  29. return _scrollRect;
  30. }
  31. }
  32. RectTransform rectTransform {
  33. get {
  34. if (!_rectTransform)
  35. {
  36. _rectTransform = this.GetComponent<RectTransform>();
  37. }
  38. return _rectTransform;
  39. }
  40. }
  41. //偏移量
  42. public int offset = 0;
  43. //解决【Content节点的AnchorPresets在Inspector中设置成了Center,但运行时的效果却是LeftTop】的问题。
  44. Vector3 contentPosition {
  45. get {
  46. Vector3 v3 = scrollRect.content.localPosition;
  47. v3.x -= rectTransform.sizeDelta.x / 2 + offset;
  48. v3.y += rectTransform.sizeDelta.y / 2 ;
  49. return v3;
  50. }
  51. set {
  52. value.x += rectTransform.sizeDelta.x / 2 + offset;
  53. value.y -= rectTransform.sizeDelta.y / 2;
  54. scrollRect.content.localPosition = value;
  55. }
  56. }
  57. public List<RectTransform> itemList = new List<RectTransform>();
  58. RectTransform minItem {
  59. get {
  60. RectTransform bestItem = null;
  61. foreach (RectTransform item in itemList)
  62. {
  63. if (!bestItem || item.localPosition.x < bestItem.localPosition.x)
  64. {
  65. bestItem = item;
  66. }
  67. }
  68. return bestItem;
  69. }
  70. }
  71. RectTransform maxItem {
  72. get {
  73. RectTransform bestItem = null;
  74. foreach (RectTransform item in itemList)
  75. {
  76. if (!bestItem || item.localPosition.x > bestItem.localPosition.x)
  77. {
  78. bestItem = item;
  79. }
  80. }
  81. return bestItem;
  82. }
  83. }
  84. public int startIndex = 0;
  85. public int spacing = 0;
  86. public bool openReceiveItemViewInfo = false;
  87. public UnityEvent<RectTransform, Vector3, Vector2> onReceiveItemViewInfo;
  88. [Tooltip("以下节点将作为预制体,不加入ItemList中作为滚动内容,组件初始化时会隐藏预制体。")]
  89. int currentItemIndex = -1;
  90. void SetCurrentItemIndex(int index)
  91. {
  92. if (index != currentItemIndex)
  93. {
  94. currentItemIndex = index;
  95. onItemChange.Invoke(itemList[index], index);
  96. }
  97. }
  98. public UnityEvent<RectTransform, int> onItemChange;
  99. public List<GameObject> prefabs;
  100. void Start()
  101. {
  102. for (int i = 0; i < scrollRect.content.childCount; i++)
  103. {
  104. RectTransform item = scrollRect.content.GetChild(i).GetComponent<RectTransform>();
  105. if (prefabs.Contains(item.gameObject)) {
  106. item.gameObject.SetActive(false);
  107. } else {
  108. if (initType == InitType.Statics)
  109. {
  110. AddItem(item);
  111. }
  112. }
  113. }
  114. UpdateItems();
  115. SelectItemByStartIndex();
  116. }
  117. public void SelectItemByStartIndex()
  118. {
  119. if (startIndex >= 0 && startIndex < itemList.Count) {
  120. SetCurrentItemIndex(startIndex);
  121. MoveItemToCenter(itemList[startIndex]);
  122. }
  123. }
  124. bool snapping = false;
  125. int snapIndex = 0;
  126. void Update()
  127. {
  128. if (snapping) {
  129. if (snapIndex >= 0 && snapIndex < itemList.Count) {
  130. RectTransform item = itemList[snapIndex];
  131. // Debug.Log("snapIndex:"+ snapIndex);
  132. MoveItemToCenter(item);
  133. } else if (snapIndex >= itemList.Count) {
  134. snapIndex = itemList.Count - 1;
  135. }
  136. }
  137. }
  138. public void UpdateItems()
  139. {
  140. if (itemList.Count == 0) return;
  141. if (!scrollRect.horizontal) return;
  142. RectTransform minItemRecord = minItem;
  143. RectTransform maxItemRecord = maxItem;
  144. float centerX = -contentPosition.x;
  145. float deltaForMin = Mathf.Abs(minItemRecord.localPosition.x - centerX);
  146. float deltaForMax = Mathf.Abs(maxItemRecord.localPosition.x - centerX);
  147. if (deltaForMin < deltaForMax) {
  148. RectTransform item = minItemRecord;
  149. while (item && IsInMinBound(item))
  150. {
  151. //Debug.Log("next==== -1");
  152. item = RenderNextItem(item, -1);
  153. }
  154. } else if (deltaForMax < deltaForMin) {
  155. RectTransform item = maxItemRecord;
  156. while (item && IsInMaxBound(item))
  157. {
  158. //Debug.Log("next====1");
  159. item = RenderNextItem(item, 1);
  160. }
  161. }
  162. if (openReceiveItemViewInfo)
  163. {
  164. Vector3 contentPoint = contentPosition;
  165. foreach (var item in itemList)
  166. {
  167. Vector3 positinInView = contentPoint + item.localPosition;
  168. Vector2 viewSize = rectTransform.sizeDelta;
  169. onReceiveItemViewInfo.Invoke(item, positinInView, viewSize);
  170. }
  171. }
  172. }
  173. bool IsInMinBound(RectTransform item)
  174. {
  175. return item.localPosition.x + contentPosition.x - item.sizeDelta.x / 2
  176. > -rectTransform.sizeDelta.x / 2;
  177. }
  178. bool IsInMaxBound(RectTransform item)
  179. {
  180. return item.localPosition.x + contentPosition.x + item.sizeDelta.x / 2
  181. < rectTransform.sizeDelta.x / 2;
  182. }
  183. bool IsOutBound(RectTransform item)
  184. {
  185. if (item.localPosition.x + contentPosition.x + item.sizeDelta.x / 2
  186. < -rectTransform.sizeDelta.x / 2) return true;
  187. if (item.localPosition.x + contentPosition.x - item.sizeDelta.x / 2
  188. > rectTransform.sizeDelta.x / 2) return true;
  189. return false;
  190. }
  191. RectTransform RenderNextItem(RectTransform item, int relativeIndex) {
  192. int index = itemList.IndexOf(item);
  193. index += relativeIndex;
  194. if (index < 0) {
  195. index = itemList.Count - 1;
  196. } else if (index >= itemList.Count) {
  197. index = 0;
  198. }
  199. //Debug.Log("index====:"+ index);
  200. RectTransform targetItem = itemList[index];
  201. if (IsOutBound(targetItem)) {
  202. Vector3 position = targetItem.localPosition;
  203. position.x = item.localPosition.x + relativeIndex * (targetItem.sizeDelta.x + spacing);
  204. targetItem.localPosition = position;
  205. return targetItem;
  206. }
  207. return null;
  208. }
  209. public void AddItem(RectTransform item)
  210. {
  211. if (scrollRect.horizontal)
  212. {
  213. item.SetParent(scrollRect.content);
  214. Vector3 position = item.localPosition;
  215. position.x = 0;
  216. if (itemList.Count > 0) {
  217. RectTransform lastItem = itemList[itemList.Count - 1];
  218. foreach (RectTransform child in itemList)
  219. {
  220. if (child.localPosition.x > lastItem.localPosition.x) {
  221. Vector3 childPosition = child.localPosition;
  222. childPosition.x += child.sizeDelta.x + spacing;
  223. child.localPosition = childPosition;
  224. }
  225. }
  226. position.x = lastItem.localPosition.x + item.sizeDelta.x + spacing;
  227. }
  228. position.y = 0;
  229. position.z = 0;
  230. item.localPosition = position;
  231. }
  232. itemList.Add(item);
  233. }
  234. public void RemoveItem(RectTransform item)
  235. {
  236. if (itemList.IndexOf(item) == currentItemIndex) {
  237. currentItemIndex = -1;
  238. }
  239. itemList.Remove(item);
  240. if (scrollRect.horizontal)
  241. {
  242. foreach (RectTransform child in itemList)
  243. {
  244. if (child.localPosition.x > item.localPosition.x) {
  245. Vector3 childPosition = child.localPosition;
  246. childPosition.x -= child.sizeDelta.x + spacing;
  247. child.localPosition = childPosition;
  248. }
  249. }
  250. }
  251. Destroy(item.gameObject);
  252. }
  253. public void ClearItems()
  254. {
  255. foreach (var item in itemList)
  256. {
  257. Destroy(item.gameObject);
  258. }
  259. itemList.Clear();
  260. currentItemIndex = -1;
  261. }
  262. RectTransform FindNearestItemToCenter()
  263. {
  264. RectTransform bestItem = null;
  265. Vector3 position = contentPosition;
  266. foreach(RectTransform item in itemList)
  267. {
  268. if (
  269. !bestItem ||
  270. Mathf.Abs(item.localPosition.x + position.x - 0)
  271. < Mathf.Abs(bestItem.localPosition.x + position.x - 0)
  272. ) {
  273. bestItem = item;
  274. }
  275. }
  276. return bestItem;
  277. }
  278. public void MoveNearestItemToCenter()
  279. {
  280. if (itemList.Count == 0) return;
  281. RectTransform item = FindNearestItemToCenter();
  282. snapping = true;
  283. snapIndex = itemList.IndexOf(item);
  284. SetCurrentItemIndex(snapIndex);
  285. }
  286. void MoveItemToCenter(RectTransform item)
  287. {
  288. Vector3 position = contentPosition;
  289. position.x -= (item.localPosition.x + contentPosition.x);
  290. if (Mathf.Abs(contentPosition.x - position.x) < 1) {
  291. snapping = false;
  292. }
  293. contentPosition = snapping ? Vector3.Lerp(contentPosition, position, Time.deltaTime * 5) : position;
  294. UpdateItems();
  295. }
  296. public void MoveNextToCenter(int direction)
  297. {
  298. if (itemList.Count == 0) return;
  299. if (direction == 0)
  300. {
  301. MoveNearestItemToCenter();
  302. return;
  303. }
  304. RectTransform item = FindNearestItemToCenter();
  305. int index = itemList.IndexOf(item);
  306. if (direction < 0)
  307. {
  308. index--;
  309. //int nextIndex = index - Mathf.Abs(direction);
  310. //index = nextIndex >= 0 ? nextIndex : itemList.Count + nextIndex;
  311. }
  312. if (direction > 0)
  313. {
  314. //int nextIndex = index + Mathf.Abs(direction);
  315. // index = nextIndex < itemList.Count ? nextIndex : nextIndex - itemList.Count;
  316. index++;
  317. }
  318. if (index < 0)
  319. {
  320. index = itemList.Count - 1;
  321. }
  322. if (index >= itemList.Count)
  323. {
  324. index = 0;
  325. }
  326. //item = itemList[index];
  327. snapping = true;
  328. snapIndex = index;
  329. SetCurrentItemIndex(snapIndex);
  330. }
  331. public void OnBeginDrag(PointerEventData eventData) {
  332. snapping = false;
  333. }
  334. public void OnDrag(PointerEventData eventData)
  335. {
  336. UpdateItems();
  337. }
  338. public void OnEndDrag(PointerEventData eventData)
  339. {
  340. MoveNearestItemToCenter();
  341. }
  342. public void OnPointerDown(PointerEventData eventData) {
  343. snapping = false;
  344. }
  345. public void OnPointerUp(PointerEventData eventData) {
  346. snapping = true;
  347. }
  348. }
  349. }