Unity中实现ViewPage、ScrollSnap

之前分享过一片关于弹性滚动列表的文章: Unity中实现弹性滚动列表基于UGUI

这次分享一个基于ScrollView的滑屏列表。移动原生平台应该称为ViewPage吧。先看一下预览。

(作者链接:Unity Forums

image

完整代码如下:


using System;
using UnityEngine.EventSystems;

    [RequireComponent (typeof (ScrollRect))]
    [AddComponentMenu ("UI/Extensions/Horizontal Scroll Snap")]
    public class HorizontalScrollSnap : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler {
        private Transform _screensContainer;

        private int _screens = 1;
        private int _startingScreen = 1;

        private bool _fastSwipeTimer = false;
        private int _fastSwipeCounter = 0;
        private int _fastSwipeTarget = 30;

        private System.Collections.Generic.List<Vector3> _positions;
        private ScrollRect _scroll_rect;
        private Vector3 _lerp_target;
        private bool _lerp;

        private int _containerSize;

        [Tooltip ("The gameobject that contains toggles which suggest pagination. (optional)")]
        public GameObject Pagination;

        [Tooltip ("Button to go to the next page. (optional)")]
        public GameObject NextButton;
        [Tooltip ("Button to go to the previous page. (optional)")]
        public GameObject PrevButton;

        public Boolean UseFastSwipe = true;
        public int FastSwipeThreshold = 100;

        private bool _startDrag = true;
        private Vector3 _startPosition = new Vector3 ();
        private int _currentScreen;
        public float _speed=10;

        // Use this for initialization
        void Start () {
            _scroll_rect = gameObject.GetComponent<ScrollRect> ();
            _screensContainer = _scroll_rect.content;
            DistributePages ();
            ChangeBulletsInfo(0);

            _screens = _screensContainer.childCount;
            Debug.Log(_scroll_rect.horizontalNormalizedPosition);

            _lerp = false;

            _positions = new System.Collections.Generic.List<Vector3> ();

            if (_screens > 0) {
                for (int i = 0; i < _screens; ++i) {
                    _scroll_rect.horizontalNormalizedPosition = (float) i / (float) (_screens - 1);
                    Debug.Log(_scroll_rect.horizontalNormalizedPosition);

                    _positions.Add (_screensContainer.localPosition);
                }
            }


            _scroll_rect.horizontalNormalizedPosition = (float) (_startingScreen - 1) / (float) (_screens - 1);

            _containerSize = (int) _screensContainer.gameObject.GetComponent<RectTransform> ().offsetMax.x;

            Debug.Log(_scroll_rect.horizontalNormalizedPosition);

            if (NextButton)
                NextButton.GetComponent<Button> ().onClick.AddListener (() => { NextScreen (); });

            if (PrevButton)
                PrevButton.GetComponent<Button> ().onClick.AddListener (() => { PreviousScreen (); });
        }

        void Update () {
            if (_lerp) {
                _screensContainer.localPosition = Vector3.Lerp (_screensContainer.localPosition, _lerp_target, _speed * Time.deltaTime);
                if (Vector3.Distance (_screensContainer.localPosition, _lerp_target) < 0.001f) {
                    _lerp = false;
                }

                //change the info bullets at the bottom of the screen. Just for visual effect
                if (Vector3.Distance (_screensContainer.localPosition, _lerp_target) < 10f) {
                    ChangeBulletsInfo (CurrentScreen ());
                }
            }

            if (_fastSwipeTimer) {
                _fastSwipeCounter++;
            }

        }

        private bool fastSwipe = false; 

        //下一页
        public void NextScreen () {
            if (CurrentScreen () < _screens - 1) {

                _lerp = true;
                _lerp_target = _positions[CurrentScreen () + 1];

                ChangeBulletsInfo (CurrentScreen () + 1);
            }
        }

        //上一页
        public void PreviousScreen () {
            if (CurrentScreen () > 0) {
                Debug.Log(CurrentScreen());
                _lerp = true;
                _lerp_target = _positions[CurrentScreen () - 1];

                ChangeBulletsInfo (CurrentScreen () - 1);
            }
        }

        private void NextScreenCommand () {
            if (_currentScreen < _screens - 1) {
                _lerp = true;
                _lerp_target = _positions[_currentScreen + 1];

                ChangeBulletsInfo (_currentScreen + 1);
            }
        }
        private void PrevScreenCommand () {
            if (_currentScreen > 0) {
                _lerp = true;
                _lerp_target = _positions[_currentScreen - 1];

                ChangeBulletsInfo (_currentScreen - 1);
            }
        }

        //获取回到指定位置的坐标
        private Vector3 FindClosestFrom (Vector3 start, System.Collections.Generic.List<Vector3> positions) {
            Vector3 closest = Vector3.zero;
            float distance = Mathf.Infinity;

            foreach (Vector3 position in _positions) {
                if (Vector3.Distance (start, position) < distance) {
                    distance = Vector3.Distance (start, position);
                    closest = position;
                }
            }

            return closest;
        }

        //返回当前屏幕的标识索引
        public int CurrentScreen () {
            //此处需要将_screenContainer的Anchor设置Min(0,0)、Max(1,1);
            float absPoz = Math.Abs (_screensContainer.gameObject.GetComponent<RectTransform> ().offsetMin.x);

            absPoz = Mathf.Clamp (absPoz, 1, _containerSize - 1);

            float calc = (absPoz / _containerSize) * _screens;

            return (int) calc;
        }
        //改变底部标识
        private void ChangeBulletsInfo (int currentScreen) {
            if (Pagination)
                for (int i = 0; i < Pagination.transform.childCount; i++) {
                    Pagination.transform.GetChild (i).GetComponent<Toggle> ().isOn = (currentScreen == i) ?
                        true :
                        false;
                }
        }

        //基于品目分辨率改变——screensContainer子物体的坐标和位置
        private void DistributePages () {
            int _offset = 0;
            int _step = Screen.width;
            int _dimension = 0;

            int currentXPosition = 0;

            for (int i = 0; i < _screensContainer.transform.childCount; i++) {
                RectTransform child = _screensContainer.transform.GetChild (i).gameObject.GetComponent<RectTransform> ();
                currentXPosition = _offset + i * _step;
                child.anchoredPosition = new Vector2 (currentXPosition, 0f);
                child.sizeDelta = new Vector2 (gameObject.GetComponent<RectTransform> ().sizeDelta.x, gameObject.GetComponent<RectTransform> ().sizeDelta.y);
            }

            _dimension = currentXPosition + _offset * -1;

            _screensContainer.GetComponent<RectTransform> ().offsetMax = new Vector2 (_dimension, 0f);
        }

        #region Interfaces
        public void OnBeginDrag (PointerEventData eventData) {
            _startPosition = _screensContainer.localPosition;
            _fastSwipeCounter = 0;
            _fastSwipeTimer = true;
            _currentScreen = CurrentScreen ();
        }

        public void OnEndDrag (PointerEventData eventData) {
            _startDrag = true;
            if (_scroll_rect.horizontal) {
                if (UseFastSwipe) {
                    fastSwipe = false;
                    _fastSwipeTimer = false;
                    if (_fastSwipeCounter <= _fastSwipeTarget) {
                        if (Math.Abs (_startPosition.x - _screensContainer.localPosition.x) > FastSwipeThreshold) {
                            fastSwipe = true;
                        }
                    }
                    if (fastSwipe) {
                        if (_startPosition.x - _screensContainer.localPosition.x > 0) {
                            NextScreenCommand ();
                        } else {
                            PrevScreenCommand ();
                        }
                    } else {
                        _lerp = true;
                        _lerp_target = FindClosestFrom (_screensContainer.localPosition, _positions);
                    }
                } else {
                    _lerp = true;
                    _lerp_target = FindClosestFrom (_screensContainer.localPosition, _positions);
                }
            }
        }

        public void OnDrag (PointerEventData eventData) {
            _lerp = false;
            if (_startDrag) {
                OnBeginDrag (eventData);
                _startDrag = false;
            }
        }
        #endregion
    }

Demo地址:扫码=>历史消息=>当前文章末尾获取

码码小虫

阅读原文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值