CarouselImage.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. using UnityEngine.UI;
  2. using UnityEngine.EventSystems;
  3. using System;
  4. using UnityEngine;
  5. /// <summary>
  6. /// 图片轮播组件
  7. /// </summary>
  8. public class CarouselImage : UIBehaviour, IBeginDragHandler, IInitializePotentialDragHandler, IDragHandler, IEndDragHandler, ICanvasElement
  9. {
  10. /// <summary>
  11. /// 滚动方向H or V
  12. /// </summary>
  13. public enum AxisType
  14. {
  15. Horizontal,
  16. Vertical
  17. }
  18. /// <summary>
  19. /// 图片轮播方向
  20. /// </summary>
  21. public enum LoopDirType
  22. {
  23. RightOrUp = -1,
  24. LeftOrDown = 1,
  25. }
  26. /// <summary>
  27. /// 子物体size
  28. /// </summary>
  29. public Vector2 mCellSize;
  30. /// <summary>
  31. /// 子物体间隔
  32. /// </summary>
  33. public Vector2 mSpacing;
  34. /// <summary>
  35. /// 方向
  36. /// </summary>
  37. public AxisType MMoveAxisType;
  38. /// <summary>
  39. /// 轮播方向-- 1为向左移动,-1为向右移动
  40. /// </summary>
  41. public LoopDirType mLoopDirType = LoopDirType.LeftOrDown;
  42. /// <summary>
  43. /// Tween时的步数
  44. /// </summary>
  45. [Range(1, 500)]
  46. public int mTweenStepNum = 150;
  47. /// <summary>
  48. /// 自动轮播
  49. /// </summary>
  50. public bool mAutoLoop = false;
  51. /// <summary>
  52. /// 可否拖动
  53. /// </summary>
  54. public bool mDrag = false;
  55. /// <summary>
  56. /// 下一次播放间隔时间
  57. /// </summary>
  58. public float mLoopSpaceTime = 1;
  59. /// <summary>
  60. /// 位于正中的子元素变化的事件,参数为index
  61. /// </summary>
  62. [HideInInspector]
  63. public Action<int> mOnIndexChange;
  64. /// <summary>
  65. /// 当前处于正中的元素
  66. /// </summary>
  67. public int CurrentIndex
  68. {
  69. get
  70. {
  71. return m_index;
  72. }
  73. }
  74. private bool m_Dragging = false;
  75. private bool m_IsNormalizing = false;
  76. private Vector2 m_CurrentPos;
  77. private int m_currentStep = 0;
  78. private RectTransform viewRectTran;
  79. private Vector2 m_PrePos;
  80. private int m_index = 0, m_preIndex = 0;
  81. private RectTransform header;
  82. private bool contentCheckCache = true;
  83. private float currTimeDelta = 0;
  84. private float viewRectXMin
  85. {
  86. get
  87. {
  88. Vector3[] v = new Vector3[4];
  89. viewRectTran.GetWorldCorners(v);
  90. return v[0].x;
  91. }
  92. }
  93. private float viewRectXMax
  94. {
  95. get
  96. {
  97. Vector3[] v = new Vector3[4];
  98. viewRectTran.GetWorldCorners(v);
  99. return v[3].x;
  100. }
  101. }
  102. private float viewRectYMin
  103. {
  104. get
  105. {
  106. Vector3[] v = new Vector3[4];
  107. viewRectTran.GetWorldCorners(v);
  108. return v[0].y;
  109. }
  110. }
  111. private float viewRectYMax
  112. {
  113. get
  114. {
  115. Vector3[] v = new Vector3[4];
  116. viewRectTran.GetWorldCorners(v);
  117. return v[2].y;
  118. }
  119. }
  120. public int CellCount
  121. {
  122. get
  123. {
  124. return transform.childCount;
  125. }
  126. }
  127. protected override void Awake()
  128. {
  129. base.Awake();
  130. viewRectTran = GetComponent<RectTransform>();
  131. header = GetChild(viewRectTran, 0);
  132. }
  133. public void resizeChildren()
  134. {
  135. Vector2 delta;
  136. if (MMoveAxisType == AxisType.Horizontal)
  137. {
  138. delta = new Vector2(mCellSize.x + mSpacing.x, 0);
  139. }
  140. else
  141. {
  142. delta = new Vector2(0, mCellSize.y + mSpacing.y);
  143. }
  144. for (int i = 0; i < CellCount; i++)
  145. {
  146. var t = GetChild(viewRectTran, i);
  147. if (t)
  148. {
  149. t.localPosition = delta * i;
  150. t.sizeDelta = mCellSize;
  151. }
  152. }
  153. m_IsNormalizing = false;
  154. m_CurrentPos = Vector2.zero;
  155. m_currentStep = 0;
  156. }
  157. /// <summary>
  158. /// 加子物体到当前列表的最后面
  159. /// </summary>
  160. /// <param name="t"></param>
  161. public virtual void AddChild(RectTransform t)
  162. {
  163. if (t != null)
  164. {
  165. t.SetParent(viewRectTran, false);
  166. t.SetAsLastSibling();
  167. Vector2 delta;
  168. if (MMoveAxisType == AxisType.Horizontal)
  169. {
  170. delta = new Vector2(mCellSize.x + mSpacing.x, 0);
  171. }
  172. else
  173. {
  174. delta = new Vector2(0, mCellSize.y + mSpacing.y);
  175. }
  176. if (CellCount == 0)
  177. {
  178. t.localPosition = Vector3.zero;
  179. header = t;
  180. }
  181. else
  182. {
  183. t.localPosition = delta + (Vector2)GetChild(viewRectTran, CellCount - 1).localPosition;
  184. header = GetChild(viewRectTran, 0);
  185. }
  186. }
  187. }
  188. protected override void OnEnable()
  189. {
  190. base.OnEnable();
  191. mOnIndexChange += OnChangeIndex;
  192. resizeChildren();
  193. //return;
  194. if (Application.isPlaying)
  195. {
  196. if (ContentIsLongerThanRect())
  197. {
  198. int s;
  199. do
  200. {
  201. s = GetBoundaryState();
  202. LoopCell(s);
  203. } while (s != 0);
  204. }
  205. }
  206. }
  207. protected override void OnDisable()
  208. {
  209. base.OnDisable();
  210. mOnIndexChange -= OnChangeIndex;
  211. }
  212. protected virtual void Update()
  213. {
  214. if (ContentIsLongerThanRect())
  215. {
  216. //实现在必要时loop子元素
  217. if (Application.isPlaying)
  218. {
  219. int s = GetBoundaryState();
  220. LoopCell(s);
  221. }
  222. //缓动回指定位置
  223. if (m_IsNormalizing && EnsureListCanAdjust())
  224. {
  225. if (m_currentStep == mTweenStepNum)
  226. {
  227. m_IsNormalizing = false;
  228. m_currentStep = 0;
  229. m_CurrentPos = Vector2.zero;
  230. return;
  231. }
  232. Vector2 delta = m_CurrentPos / mTweenStepNum;
  233. m_currentStep++;
  234. TweenToCorrect(-delta);
  235. }
  236. //自动loop
  237. if (mAutoLoop && !m_IsNormalizing && EnsureListCanAdjust())
  238. {
  239. currTimeDelta += Time.deltaTime;
  240. if (currTimeDelta > mLoopSpaceTime)
  241. {
  242. currTimeDelta = 0;
  243. MoveToIndex(m_index + (int)mLoopDirType);
  244. }
  245. }
  246. //检测index是否变化
  247. if (MMoveAxisType == AxisType.Horizontal)
  248. {
  249. m_index = (int)(header.localPosition.x / (mCellSize.x + mSpacing.x - 1));
  250. }
  251. else
  252. {
  253. m_index = (int)(header.localPosition.y / (mCellSize.y + mSpacing.y - 1));
  254. }
  255. if (m_index <= 0)
  256. {
  257. m_index = Mathf.Abs(m_index);
  258. }
  259. else
  260. {
  261. m_index = CellCount - m_index;
  262. }
  263. if (m_index != m_preIndex)
  264. {
  265. if (mOnIndexChange != null)
  266. {
  267. mOnIndexChange(m_index);
  268. }
  269. }
  270. m_preIndex = m_index;
  271. }
  272. }
  273. public virtual void OnBeginDrag(PointerEventData eventData)
  274. {
  275. if (!mDrag || !contentCheckCache)
  276. {
  277. return;
  278. }
  279. Vector2 vector;
  280. if (((eventData.button == PointerEventData.InputButton.Left) && this.IsActive()) && RectTransformUtility.ScreenPointToLocalPointInRectangle(this.viewRectTran, eventData.position, eventData.pressEventCamera, out vector))
  281. {
  282. this.m_Dragging = true;
  283. m_PrePos = vector;
  284. currTimeDelta = 0;
  285. }
  286. }
  287. public virtual void OnInitializePotentialDrag(PointerEventData eventData)
  288. {
  289. if (!mDrag)
  290. {
  291. return;
  292. }
  293. return;
  294. }
  295. public virtual void OnDrag(PointerEventData eventData)
  296. {
  297. if (!mDrag || !contentCheckCache)
  298. {
  299. return;
  300. }
  301. Vector2 vector;
  302. if (((eventData.button == PointerEventData.InputButton.Left) && this.IsActive()) && RectTransformUtility.ScreenPointToLocalPointInRectangle(this.viewRectTran, eventData.position, eventData.pressEventCamera, out vector))
  303. {
  304. m_IsNormalizing = false;
  305. m_CurrentPos = Vector2.zero;
  306. m_currentStep = 0;
  307. Vector2 vector2 = vector - this.m_PrePos;
  308. Vector2 vec = CalculateOffset(vector2);
  309. this.SetContentPosition(vec);
  310. m_PrePos = vector;
  311. }
  312. }
  313. /// <summary>
  314. /// 移动到指定索引
  315. /// </summary>
  316. /// <param name="ind"></param>
  317. public virtual void MoveToIndex(int ind)
  318. {
  319. if (m_IsNormalizing)
  320. {
  321. return;
  322. }
  323. if (ind == m_index)
  324. {
  325. return;
  326. }
  327. this.m_IsNormalizing = true;
  328. Vector2 offset;
  329. if (MMoveAxisType == AxisType.Horizontal)
  330. {
  331. offset = new Vector2(mCellSize.x + mSpacing.x, 0);
  332. }
  333. else
  334. {
  335. offset = new Vector2(0, mCellSize.y + mSpacing.y);
  336. }
  337. var delta = CalcCorrectDeltaPos();
  338. int vindex = m_index;
  339. m_CurrentPos = delta + offset * (ind - vindex);
  340. m_currentStep = 0;
  341. }
  342. private Vector2 CalculateOffset(Vector2 delta)
  343. {
  344. if (MMoveAxisType == AxisType.Horizontal)
  345. {
  346. delta.y = 0;
  347. }
  348. else
  349. {
  350. delta.x = 0;
  351. }
  352. return delta;
  353. }
  354. private void SetContentPosition(Vector2 position)
  355. {
  356. foreach (RectTransform i in viewRectTran)
  357. {
  358. i.localPosition += (Vector3)position;
  359. }
  360. return;
  361. }
  362. public virtual void OnEndDrag(PointerEventData eventData)
  363. {
  364. if (!mDrag || !contentCheckCache)
  365. {
  366. return;
  367. }
  368. this.m_Dragging = false;
  369. this.m_IsNormalizing = true;
  370. m_CurrentPos = CalcCorrectDeltaPos();
  371. m_currentStep = 0;
  372. }
  373. public virtual void Rebuild(CanvasUpdate executing)
  374. {
  375. return;
  376. }
  377. /// <summary>
  378. /// List是否处于可自由调整状态
  379. /// </summary>
  380. /// <returns></returns>
  381. public virtual bool EnsureListCanAdjust()
  382. {
  383. return !m_Dragging && ContentIsLongerThanRect();
  384. }
  385. /// <summary>
  386. /// 内容是否比显示范围大
  387. /// </summary>
  388. /// <returns></returns>
  389. public virtual bool ContentIsLongerThanRect()
  390. {
  391. float contentLen;
  392. float rectLen;
  393. if (MMoveAxisType == AxisType.Horizontal)
  394. {
  395. contentLen = CellCount * (mCellSize.x + mSpacing.x) - mSpacing.x;
  396. rectLen = viewRectTran.rect.xMax - viewRectTran.rect.xMin;
  397. }
  398. else
  399. {
  400. contentLen = CellCount * (mCellSize.y + mSpacing.y) - mSpacing.y;
  401. rectLen = viewRectTran.rect.yMax - viewRectTran.rect.yMin;
  402. }
  403. contentCheckCache = contentLen > rectLen;
  404. return contentCheckCache;
  405. }
  406. /// <summary>
  407. /// 检测边界情况,分为0未触界,-1左(下)触界,1右(上)触界
  408. /// </summary>
  409. /// <returns></returns>
  410. public virtual int GetBoundaryState()
  411. {
  412. RectTransform left;
  413. RectTransform right;
  414. left = GetChild(viewRectTran, 0);
  415. right = GetChild(viewRectTran, CellCount - 1);
  416. Vector3[] l = new Vector3[4];
  417. left.GetWorldCorners(l);
  418. Vector3[] r = new Vector3[4];
  419. right.GetWorldCorners(r);
  420. if (MMoveAxisType == AxisType.Horizontal)
  421. {
  422. if (l[0].x >= viewRectXMin)
  423. {
  424. return -1;
  425. }
  426. else if (r[3].x < viewRectXMax)
  427. {
  428. return 1;
  429. }
  430. }
  431. else
  432. {
  433. if (l[0].y >= viewRectYMin)
  434. {
  435. return -1;
  436. }
  437. else if (r[1].y < viewRectYMax)
  438. {
  439. return 1;
  440. }
  441. }
  442. return 0;
  443. }
  444. /// <summary>
  445. /// Loop列表,分为-1把最右(上)边一个移到最左(下)边,1把最左(下)边一个移到最右(上)边
  446. /// </summary>
  447. /// <param name="dir"></param>
  448. protected virtual void LoopCell(int dir)
  449. {
  450. if (dir == 0)
  451. {
  452. return;
  453. }
  454. RectTransform MoveCell;
  455. RectTransform Tarborder;
  456. Vector2 TarPos;
  457. if (dir == 1)
  458. {
  459. MoveCell = GetChild(viewRectTran, 0);
  460. Tarborder = GetChild(viewRectTran, CellCount - 1);
  461. MoveCell.SetSiblingIndex(CellCount - 1);
  462. }
  463. else
  464. {
  465. Tarborder = GetChild(viewRectTran, 0);
  466. MoveCell = GetChild(viewRectTran, CellCount - 1);
  467. MoveCell.SetSiblingIndex(0);
  468. }
  469. if (MMoveAxisType == AxisType.Horizontal)
  470. {
  471. TarPos = Tarborder.localPosition + new Vector3((mCellSize.x + mSpacing.x) * dir, 0, 0);
  472. }
  473. else
  474. {
  475. TarPos = (Vector2)Tarborder.localPosition + new Vector2(0, (mCellSize.y + mSpacing.y) * dir);
  476. }
  477. MoveCell.localPosition = TarPos;
  478. }
  479. /// <summary>
  480. /// 计算一个最近的正确位置
  481. /// </summary>
  482. /// <returns></returns>
  483. public virtual Vector2 CalcCorrectDeltaPos()
  484. {
  485. Vector2 delta = Vector2.zero;
  486. float distance = float.MaxValue;
  487. foreach (RectTransform i in viewRectTran)
  488. {
  489. var td = Mathf.Abs(i.localPosition.x) + Mathf.Abs(i.localPosition.y);
  490. if (td <= distance)
  491. {
  492. distance = td;
  493. delta = i.localPosition;
  494. }
  495. else
  496. {
  497. break;
  498. }
  499. }
  500. return delta;
  501. }
  502. /// <summary>
  503. /// 移动指定增量
  504. /// </summary>
  505. protected virtual void TweenToCorrect(Vector2 delta)
  506. {
  507. foreach (RectTransform i in viewRectTran)
  508. {
  509. i.localPosition += (Vector3)delta;
  510. }
  511. }
  512. private static RectTransform GetChild(RectTransform parent, int index)
  513. {
  514. if (parent == null || index >= parent.childCount)
  515. {
  516. return null;
  517. }
  518. return parent.GetChild(index) as RectTransform;
  519. }
  520. public void LayoutComplete()
  521. {
  522. }
  523. public void GraphicUpdateComplete()
  524. {
  525. }
  526. /// <summary>
  527. /// 当前中心位置index回调
  528. /// </summary>
  529. /// <param name="index"></param>
  530. public void OnChangeIndex(int index)
  531. {
  532. }
  533. }