Unity 輪播

/// 主要關注屬性、事件及函數:
///     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]
// UIBehaviour, IEventSystemHandler, IBeginDragHandler, IInitializePotentialDragHandler, IDragHandler, IEndDragHandler
public class Carousel : UIBehaviour, IEventSystemHandler
{
    public Button btn1;
    public Button btn2;
    protected virtual void Start()
    {
        btn1.onClick.AddListener(OnClickOne);
        btn2.onClick.AddListener(OnClickTwo);
    }
    /// <summary>
    /// 子物體size
    /// </summary>
    public Vector2 CellSize;
    /// <summary>
    /// 子物體間隔
    /// </summary>
    public Vector2 Spacing;
    /// <summary>
    /// 方向
    /// </summary>
    public Axis MoveAxis;
    /// <summary>
    /// Tween時的步數
    /// </summary>
    public int TweenStepCount = 10;
    /// <summary>
    /// 自動輪播
    /// </summary>
    public bool AutoLoop = false;
    /// <summary>
    /// 輪播間隔
    /// </summary>
    public float LoopSpace = 1;
    /// <summary>
    /// 輪播方向--1爲向左移動,-1爲向右移動
    /// </summary>
    public int LoopDir = 1;
    /// <summary>
    /// 可否拖動
    /// </summary>
   // public bool Drag = true;
    /// <summary>
    /// 位於正中的子元素變化的事件,參數爲index
    /// </summary>
    public Action<int> OnIndexChange;
    /// <summary>
    /// 當前處於正中的元素
    /// </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>
    /// 加子物體到當前列表的最後面
    /// </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())
        {
            //實現在必要時loop子元素
            if (Application.isPlaying)
            {
                int s = GetBoundaryState();
                LoopCell(s);
            }
            //緩動回指定位置
            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);
            }
            //自動loop
            //if (AutoLoop && !m_IsNormalizing && EnsureListCanAdjust())
            //{
            //    currTimeDelta += Time.deltaTime;
            //    if (currTimeDelta > LoopSpace)
            //    {
            //        currTimeDelta = 0;
            //        MoveToIndex(m_index + LoopDir);
            //    }
            //}
            //檢測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>
    /// 移動到指定索引
    /// </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是否處於可自由調整狀態
    /// </summary>
    /// <returns></returns>
    public virtual bool EnsureListCanAdjust()
    {
        return !m_Dragging && ContentIsLongerThanRect();
    }
    /// <summary>
    /// 內容是否比顯示範圍大
    /// </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>
    /// 檢測邊界情況,分爲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把最右(上)邊一個移到最左(下)邊,1把最左(下)邊一個移到最右(上)邊
    /// </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>
    /// 計算一個最近的正確位置
    /// </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>
    /// 移動指定增量
    /// </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;
    }
    public void OnClickOne()
    {
        if ( !m_IsNormalizing && EnsureListCanAdjust())
        {
            //currTimeDelta += Time.deltaTime;
           // if (currTimeDelta > LoopSpace)
            //{
               // currTimeDelta = 0;
                MoveToIndex(m_index + LoopDir);
          //  }
        }
    }
    public void OnClickTwo()
    {
        LoopDir = -1;
        if (!m_IsNormalizing && EnsureListCanAdjust())
        {
            MoveToIndex(m_index + LoopDir);
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章