UGUI輪播圖組件實(shí)現(xiàn)方法詳解
本文實(shí)例為大家分享了UGUI輪播圖組件實(shí)現(xiàn)的具體代碼,供大家參考,具體內(nèi)容如下
要用到,于是就自已做了一個(gè),自認(rèn)為封裝上還是OK的,開(kāi)發(fā)于unity5.1.2。
支持自動(dòng)輪播、手勢(shì)切換、代碼調(diào)用切換,支持水平和豎直兩個(gè)方向以及正負(fù)方向輪播,輪播索引改變有回調(diào)可以用,也可以獲取到當(dāng)前處于正中的子元素。
要注意的是,向輪播列表中加入新元素不能直接setparent,要調(diào)用該組件的AddChild方法
下面是鄙人的代碼:
/// 主要關(guān)注屬性、事件及函數(shù): /// public int CurrentIndex; /// public Action<int> OnIndexChange; /// public virtual void MoveToIndex(int ind); /// public virtual void AddChild(RectTransform t); /// by yangxun using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; using UnityEngine.EventSystems; using System; /// <summary> /// 輪播圖組件 /// </summary> [RequireComponent(typeof(RectTransform)), ExecuteInEditMode] public class Carousel : UIBehaviour, IEventSystemHandler, IBeginDragHandler, IInitializePotentialDragHandler, IDragHandler, IEndDragHandler, ICanvasElement { /// <summary> /// 子物體size /// </summary> public Vector2 CellSize; /// <summary> /// 子物體間隔 /// </summary> public Vector2 Spacing; /// <summary> /// 方向 /// </summary> public Axis MoveAxis; /// <summary> /// Tween時(shí)的步數(shù) /// </summary> public int TweenStepCount = 10; /// <summary> /// 自動(dòng)輪播 /// </summary> public bool AutoLoop = false; /// <summary> /// 輪播間隔 /// </summary> public float LoopSpace = 1; /// <summary> /// 輪播方向--1為向左移動(dòng),-1為向右移動(dòng) /// </summary> public int LoopDir = 1; /// <summary> /// 可否拖動(dòng) /// </summary> public bool Drag = true; /// <summary> /// 位于正中的子元素變化的事件,參數(shù)為index /// </summary> public Action<int> OnIndexChange; /// <summary> /// 當(dāng)前處于正中的元素 /// </summary> public int CurrentIndex { get { return m_index; } } private bool m_Dragging = false; private bool m_IsNormalizing = false; private Vector2 m_CurrentPos; private int m_currentStep = 0; private RectTransform viewRectTran; private Vector2 m_PrePos; private int m_index = 0,m_preIndex = 0; private RectTransform header; private bool contentCheckCache = true; private float currTimeDelta = 0; private float viewRectXMin { get{ Vector3[] v = new Vector3[4]; viewRectTran.GetWorldCorners(v); return v[0].x; } } private float viewRectXMax { get { Vector3[] v = new Vector3[4]; viewRectTran.GetWorldCorners(v); return v[3].x; } } private float viewRectYMin { get { Vector3[] v = new Vector3[4]; viewRectTran.GetWorldCorners(v); return v[0].y; } } private float viewRectYMax { get { Vector3[] v = new Vector3[4]; viewRectTran.GetWorldCorners(v); return v[2].y; } } public int CellCount { get { return transform.childCount; } } protected override void Awake() { base.Awake(); viewRectTran = GetComponent<RectTransform>(); header = GetChild(viewRectTran, 0); } public void resizeChildren() { //init child size and pos Vector2 delta; if (MoveAxis == Axis.Horizontal) { delta = new Vector2(CellSize.x + Spacing.x, 0); } else { delta = new Vector2(0, CellSize.y + Spacing.y); } for (int i = 0; i < CellCount; i++) { var t = GetChild(viewRectTran, i); if (t) { t.localPosition = delta * i; t.sizeDelta = CellSize; } } m_IsNormalizing = false; m_CurrentPos = Vector2.zero; m_currentStep = 0; } /// <summary> /// 加子物體到當(dāng)前列表的最后面 /// </summary> /// <param name="t"></param> public virtual void AddChild(RectTransform t) { if (t!=null) { t.SetParent(viewRectTran, false); t.SetAsLastSibling(); Vector2 delta; if (MoveAxis == Axis.Horizontal) { delta = new Vector2(CellSize.x + Spacing.x, 0); } else { delta = new Vector2(0, CellSize.y + Spacing.y); } if (CellCount == 0) { t.localPosition = Vector3.zero; header = t; } else { t.localPosition = delta + (Vector2)GetChild(viewRectTran,CellCount-1).localPosition; } } } protected override void OnEnable() { base.OnEnable(); resizeChildren(); return; if (Application.isPlaying) { if (ContentIsLongerThanRect()) { int s; do { s = GetBoundaryState(); LoopCell(s); } while (s != 0); } } } protected virtual void Update() { if (ContentIsLongerThanRect()) { //實(shí)現(xiàn)在必要時(shí)loop子元素 if (Application.isPlaying) { int s = GetBoundaryState(); LoopCell(s); } //緩動(dòng)回指定位置 if (m_IsNormalizing && EnsureListCanAdjust()) { if (m_currentStep == TweenStepCount) { m_IsNormalizing = false; m_currentStep = 0; m_CurrentPos = Vector2.zero; return; } Vector2 delta = m_CurrentPos/TweenStepCount; m_currentStep++; TweenToCorrect(-delta); } //自動(dòng)loop if (AutoLoop && !m_IsNormalizing && EnsureListCanAdjust()) { currTimeDelta += Time.deltaTime; if (currTimeDelta>LoopSpace) { currTimeDelta = 0; MoveToIndex(m_index + LoopDir); } } //檢測(cè)index是否變化 if (MoveAxis == Axis.Horizontal) { m_index = (int)(header.localPosition.x / (CellSize.x + Spacing.x-1)); } else { m_index = (int)(header.localPosition.y / (CellSize.y + Spacing.y-1)); } if (m_index<=0) { m_index = Mathf.Abs(m_index); } else { m_index = CellCount - m_index; } if (m_index != m_preIndex) { if (OnIndexChange != null) { OnIndexChange(m_index); } } m_preIndex = m_index; } } public virtual void OnBeginDrag(PointerEventData eventData) { if (!Drag || !contentCheckCache) { return; } Vector2 vector; if (((eventData.button == PointerEventData.InputButton.Left) && this.IsActive()) && RectTransformUtility.ScreenPointToLocalPointInRectangle(this.viewRectTran, eventData.position, eventData.pressEventCamera, out vector)) { this.m_Dragging = true; m_PrePos = vector; } } public virtual void OnInitializePotentialDrag(PointerEventData eventData) { if (!Drag) { return; } return; } public virtual void OnDrag(PointerEventData eventData) { if (!Drag || !contentCheckCache) { return; } Vector2 vector; if (((eventData.button == PointerEventData.InputButton.Left) && this.IsActive()) && RectTransformUtility.ScreenPointToLocalPointInRectangle(this.viewRectTran, eventData.position, eventData.pressEventCamera, out vector)) { m_IsNormalizing = false; m_CurrentPos = Vector2.zero; m_currentStep = 0; Vector2 vector2 = vector - this.m_PrePos; Vector2 vec = CalculateOffset(vector2); this.SetContentPosition(vec); m_PrePos = vector; } } /// <summary> /// 移動(dòng)到指定索引 /// </summary> /// <param name="ind"></param> public virtual void MoveToIndex(int ind) { if (m_IsNormalizing) { return; } //Debug.LogFormat("{0}->{1}",m_index,ind); if (ind == m_index) { return; } this.m_IsNormalizing = true; Vector2 offset; if (MoveAxis == Axis.Horizontal) { offset = new Vector2(CellSize.x + Spacing.x, 0); } else { offset = new Vector2(0, CellSize.y + Spacing.y); } var delta = CalcCorrectDeltaPos(); int vindex = m_index; m_CurrentPos = delta + offset * (ind - vindex); //m_CurrentPos = -(Vector2)header.localPosition + offset * (ind - m_index); m_currentStep = 0; } private Vector2 CalculateOffset(Vector2 delta) { if (MoveAxis == Axis.Horizontal) { delta.y = 0; } else { delta.x = 0; } return delta; } private void SetContentPosition(Vector2 position) { foreach (RectTransform i in viewRectTran) { i.localPosition += (Vector3)position; } return; } public virtual void OnEndDrag(PointerEventData eventData) { if (!Drag || !contentCheckCache) { return; } this.m_Dragging = false; this.m_IsNormalizing = true; m_CurrentPos = CalcCorrectDeltaPos(); m_currentStep = 0; } public virtual void Rebuild(CanvasUpdate executing) { return; } /// <summary> /// List是否處于可自由調(diào)整狀態(tài) /// </summary> /// <returns></returns> public virtual bool EnsureListCanAdjust() { return !m_Dragging && ContentIsLongerThanRect(); } /// <summary> /// 內(nèi)容是否比顯示范圍大 /// </summary> /// <returns></returns> public virtual bool ContentIsLongerThanRect() { float contentLen; float rectLen; if (MoveAxis == Axis.Horizontal) { contentLen = CellCount*(CellSize.x + Spacing.x) - Spacing.x; rectLen = viewRectTran.rect.xMax - viewRectTran.rect.xMin; } else { contentLen = CellCount * (CellSize.y + Spacing.y) - Spacing.y; rectLen = viewRectTran.rect.yMax - viewRectTran.rect.yMin; } contentCheckCache = contentLen > rectLen; return contentCheckCache; } /// <summary> /// 檢測(cè)邊界情況,分為0未觸界,-1左(下)觸界,1右(上)觸界 /// </summary> /// <returns></returns> public virtual int GetBoundaryState() { RectTransform left; RectTransform right; left = GetChild(viewRectTran, 0); right = GetChild(viewRectTran, CellCount - 1); Vector3[] l = new Vector3[4]; left.GetWorldCorners(l); Vector3[] r = new Vector3[4]; right.GetWorldCorners(r); if (MoveAxis == Axis.Horizontal) { if (l[0].x>=viewRectXMin) { return -1; } else if (r[3].x < viewRectXMax) { return 1; } } else { if (l[0].y >= viewRectYMin) { return -1; } else if (r[1].y < viewRectYMax) { return 1; } } return 0; } /// <summary> /// Loop列表,分為-1把最右(上)邊一個(gè)移到最左(下)邊,1把最左(下)邊一個(gè)移到最右(上)邊 /// </summary> /// <param name="dir"></param> protected virtual void LoopCell(int dir) { if (dir == 0) { return; } RectTransform MoveCell; RectTransform Tarborder; Vector2 TarPos; if (dir == 1) { MoveCell = GetChild(viewRectTran, 0); Tarborder = GetChild(viewRectTran, CellCount - 1); MoveCell.SetSiblingIndex(CellCount-1); } else { Tarborder = GetChild(viewRectTran, 0); MoveCell = GetChild(viewRectTran, CellCount - 1); MoveCell.SetSiblingIndex(0); } if (MoveAxis == Axis.Horizontal) { TarPos = Tarborder.localPosition + new Vector3((CellSize.x + Spacing.x) * dir, 0,0); } else { TarPos = (Vector2)Tarborder.localPosition + new Vector2(0, (CellSize.y + Spacing.y) * dir); } MoveCell.localPosition = TarPos; } /// <summary> /// 計(jì)算一個(gè)最近的正確位置 /// </summary> /// <returns></returns> public virtual Vector2 CalcCorrectDeltaPos() { Vector2 delta = Vector2.zero; float distance = float.MaxValue; foreach (RectTransform i in viewRectTran) { var td = Mathf.Abs(i.localPosition.x) + Mathf.Abs(i.localPosition.y); if (td<=distance) { distance = td; delta = i.localPosition; } else { break; } } return delta; } /// <summary> /// 移動(dòng)指定增量 /// </summary> protected virtual void TweenToCorrect(Vector2 delta) { foreach (RectTransform i in viewRectTran) { i.localPosition += (Vector3)delta; } } public enum Axis { Horizontal, Vertical } private static RectTransform GetChild(RectTransform parent, int index) { if (parent == null||index>=parent.childCount) { return null; } return parent.GetChild(index) as RectTransform; } }
用法和ugui的scrollrect組件是差不多的,因?yàn)楸緛?lái)在drag事件上有所借鑒
例圖如下:
另外,它不會(huì)像ugui的幾個(gè)布局組件一樣自動(dòng)去改變子元素的大小為cellsize,cellsize只是虛擬的子元素容器大小,這個(gè)要注意下。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
WPF+SkiaSharp實(shí)現(xiàn)自繪拖曳小球
WPF的拖曳效果,基本配置一下,就可以了,但是自繪的話,就得自己控制。本文將利用WPF+SkiaSharp實(shí)現(xiàn)自繪拖曳小球,感興趣的可以動(dòng)手嘗試一下2022-07-07超炫酷的WPF實(shí)現(xiàn)Loading控件效果
這篇文章主要介紹了超炫酷的WPF實(shí)現(xiàn)Loading控件效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-11-11Unity3D實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲(2)
這篇文章主要為大家詳細(xì)介紹了Unity3D實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲的第二部分,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06C#實(shí)現(xiàn)軟件監(jiān)控外部程序運(yùn)行狀態(tài)的方法
這篇文章主要介紹了C#實(shí)現(xiàn)軟件監(jiān)控外部程序運(yùn)行狀態(tài)的方法,可實(shí)現(xiàn)監(jiān)控另一個(gè)程序的運(yùn)行狀態(tài)及觸發(fā)相應(yīng)事件的功能,是非常實(shí)用的技巧,需要的朋友可以參考下2014-12-12win7中C#的winForm編程使用savefiledialog不能彈出保存窗體的解決方法
這篇文章主要介紹了win7中C#的winForm編程使用savefiledialog不能彈出保存窗體的解決方法,涉及針對(duì)線程的調(diào)用問(wèn)題,是比較實(shí)用的技巧,需要的朋友可以參考下2014-12-12C#區(qū)分中英文按照指定長(zhǎng)度截取字符串的方法
這篇文章主要介紹了C#區(qū)分中英文按照指定長(zhǎng)度截取字符串的方法,涉及C#操作字符串的正則匹配與截取等常用操作技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-03-03