Unity 自动吸附大小变化ScrollView

一.组件设置

二.代码实现


using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

namespace GameMain.Runtime
{
    [RequireComponent(typeof(ScrollRect))]
    public class ScrollViewEx : MonoBehaviour, IDragHandler, IEndDragHandler, IBeginDragHandler
    {
        private enum SnapState
        {
            None,
            Inertia,
            Reverse,
        }

        private const float SnapReverseSpeed = 100f;

        [SerializeField] private bool snap;
        [SerializeField] private bool scale;
        [SerializeField, Range(0.5f, 2f)] private float maxScale = 1.2f;
        [SerializeField, Range(0.5f, 2f)] private float minScale = 1f;

        private ScrollRect _scrollRect;
        private RectTransform _content;
        private bool _isInit;
        private Vector2 _size;
        private Vector2 _childSize;
        private float _spacing;
        private float _snapDecelerate;
        private float _offsetX;
        private Vector2 _snapTargetPos;
        private SnapState _snapState;
        private List<RectTransform> _children;

        private void Start()
        {
            Init();
        }

        private void Update()
        {
            if (!_isInit) return;
            switch (_snapState)
            {
                case SnapState.Inertia:
                    UpdateSnapInertia();
                    break;
                case SnapState.Reverse:
                    UpdateSnapReverse();
                    break;
                case SnapState.None:
                default:
                    break;
            }

            if (!_content.hasChanged || !scale) return;
            UpdateScale();
        }


        public void OnDrag(PointerEventData eventData)
        {
        }

        public void OnEndDrag(PointerEventData eventData)
        {
            StartSnap();
        }

        public void OnBeginDrag(PointerEventData eventData)
        {
            BreakSnap();
        }

        private void Init()
        {
            _isInit = false;

            _scrollRect = GetComponent<ScrollRect>();
            _content = _scrollRect.content;

            if (!_scrollRect.horizontal) return;
            _children = new List<RectTransform>();
            _size = _scrollRect.viewport.rect.size;

            for (var i = 0; i < _content.childCount; i++)
            {
                _children.Add(_content.GetChild(i).GetComponent<RectTransform>());
            }

            if (_children.Count == 0) return;

            _childSize = _children[0].rect.size;
            var layout = _content.GetComponent<HorizontalLayoutGroup>();
            _spacing = layout.spacing;
            _offsetX = _spacing + _childSize.x;

            _snapDecelerate = _scrollRect.decelerationRate;

            if (scale)
            {
                UpdateScale();
            }
            
            _isInit = true;
        }


        private void UpdateSnapInertia()
        {
            if (_scrollRect.velocity.x is <= -SnapReverseSpeed or >= SnapReverseSpeed) return;
            StartSnapReverse();
            return;
        }

        private void UpdateSnapReverse()
        {
            if (Mathf.Abs(_content.anchoredPosition.x - _snapTargetPos.x) < 1)
            {
                _content.anchoredPosition = _snapTargetPos;
                EndSnap();
                return;
            }

            _content.anchoredPosition = Vector2.Lerp(_content.anchoredPosition, _snapTargetPos, _snapDecelerate);
        }

        /// <summary>
        /// 0 -> 0
        /// 1 -> 0+gridSpace+childSize.x
        /// 2 -> 0+gridSpace*2+childSize.x*2
        /// </summary>
        private void StartSnapReverse()
        {
            _snapState = SnapState.Reverse;
            _scrollRect.StopMovement();

            var index = (int)(_content.anchoredPosition.x / _offsetX);
            _snapTargetPos.x = index * _offsetX;
            _snapTargetPos.y = _content.anchoredPosition.y;
        }

        private void EndSnap()
        {
            if (_snapState == SnapState.None) return;

            _scrollRect.StopMovement();
            _snapState = SnapState.None;
        }

        private void BreakSnap()
        {
            _snapState = SnapState.None;
        }

        private void StartSnap()
        {
            if (!snap) return;
            if (_children.Count == 0) return;
            _snapState = SnapState.Inertia;
        }

        private void UpdateScale()
        {
            var tempCenter = Mathf.Abs(_content.anchoredPosition.x) + _size.x / 2;
            foreach (var children in _children)
            {
                if (!children.gameObject.activeSelf)
                {
                    continue;
                }
                var tempOffset = Mathf.Abs(tempCenter - children.anchoredPosition.x);
                if (tempOffset > _size.x / 2 + _childSize.x)
                {
                    continue;
                }

                var tempScale = maxScale - (tempOffset / _size.x);
                if (tempScale < minScale)
                {
                    tempScale = minScale;
                }

                children.localScale = new Vector3(tempScale, tempScale, 1);
            }
        }
    }
}

三.实现效果

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值