当前位置:网站首页>【Unity UGUI】ScrollRect 动态缩放格子大小,自动定位到中间的格子
【Unity UGUI】ScrollRect 动态缩放格子大小,自动定位到中间的格子
2022-07-04 15:33:00 【InfoQ】
使用说明:
效果演示:
实现原理:
实现代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;
[RequireComponent(typeof(ScrollRect))]
public class ScrollViewExtras : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
private enum SnapState
{
None,
Inertia,
Reverse,
}
public Action OnScrollStartDrag;
public Action<int> OnScrollEndDrag;
public Action<int> OnSelectGridChanged;
[SerializeField] private float gridSpace = 20;
[SerializeField] private bool isSnap;
[SerializeField] private bool isScale;
[SerializeField] private AnimationCurve scaleCurve = new AnimationCurve(new Keyframe(0,1),new Keyframe(1,0.5f));
//初始化不变字段
private ScrollRect scrollRect;
private RectTransform contentRectTrans;
private Vector2 scrollHalfSize; //列表尺寸
private Vector2 gridSize;
private List<RectTransform> gridList = new List<RectTransform>();
private List<Vector2> gridCenterList = new List<Vector2>();
private float snapDecelerate;
private const float snapReverseSpeed = 500;
private bool isInited = false;
//动态变化字段
private SnapState snapState = SnapState.None;
private int curSelectIndex;
//----------
private void Start()
{
Init();
}
private void Update()
{
if(isInited)
{
switch(snapState)
{
case SnapState.Inertia:
UpdateSnapInertia();
break;
case SnapState.Reverse:
UpdateSnapReverse();
break;
default:
break;
}
if(contentRectTrans.hasChanged)
{
if(isScale)
UpdateScale();
else
UpdateSelectGrid();
}
}
}
#region --- Drag Event
public void OnBeginDrag(PointerEventData eventData)
{
OnScrollStartDrag?.Invoke();
BreakSnap();
}
public void OnDrag(PointerEventData eventData)
{
}
public void OnEndDrag(PointerEventData eventData)
{
StartSnap();
}
#endregion
public void Init()
{
scrollRect = GetComponent<ScrollRect>();
if(!scrollRect.horizontal)
Debug.LogError("目前只支持横向从左往右列表");
contentRectTrans = scrollRect.content;
if(contentRectTrans.pivot.x > 0)
Debug.LogError("目前只支持横向从左往右列表");
//scrollCenter = scrollRect.viewport.rect.center;
scrollHalfSize = scrollRect.viewport.rect.size * 0.5f;
for(int i = 0; i < contentRectTrans.childCount; i++)
{
gridList.Add(contentRectTrans.GetChild(i) as RectTransform);
}
if(gridList.Count > 0)
gridSize = gridList[0].rect.size;
snapDecelerate = scrollRect.decelerationRate;
//if(snapDecelerate < 0.1f)
// snapDecelerate = 0.1f;
if(gridList.Count == 0)
return;
//第一个格子坐标
Vector2 gridInitPos = gridList[0].anchoredPosition;
gridInitPos.x = scrollHalfSize.x - gridSize.x * 0.5f;
//格子间隔
Vector2 gridOffset = Vector2.zero;
gridOffset.x = gridSize.x + gridSpace;
//计算画布尺寸
Vector2 contentSize = contentRectTrans.rect.size;
contentSize.x = gridSize.x * gridList.Count + gridSpace * (gridList.Count - 1) + scrollHalfSize.x * 2 - gridSize.x;
//设置画布尺寸
contentRectTrans.sizeDelta = contentSize;
contentRectTrans.anchoredPosition = new Vector2(0,contentRectTrans.anchoredPosition.y);
//设置每个格子坐标
for(int i = 0; i < gridList.Count; i++)
{
gridList[i].anchoredPosition = gridInitPos + gridOffset * i;
gridList[i].anchorMin = new Vector2(0,1);
gridList[i].anchorMax = new Vector2(0,1);
gridList[i].pivot = new Vector2(0,1);
gridCenterList.Add(gridList[i].anchoredPosition + gridSize * 0.5f);
}
if(isScale)
UpdateScale();
isInited = true;
curSelectIndex = 0;
}
#region --- Snap ---
private Vector2 snapTargetPos;
private void StartSnap()
{
if(isSnap)
{
if(gridList.Count > 0)
{
snapState = SnapState.Inertia;
}
}
}
private void UpdateSnapInertia()
{
if(scrollRect.velocity.x > -snapReverseSpeed && scrollRect.velocity.x < snapReverseSpeed)
{
//反向
StartSnapReverse();
return;
}
}
private void StartSnapReverse()
{
snapState = SnapState.Reverse;
scrollRect.StopMovement();
//当前屏幕中心的画布坐标
float centerPos = Mathf.Abs(contentRectTrans.anchoredPosition.x) + scrollHalfSize.x;
float temOffset;
float minOffet = float.MaxValue;
for(int i = 0; i < gridCenterList.Count; i++)
{
if(!gridList[i].gameObject.activeSelf)
continue;
//格子中心坐标
temOffset = centerPos - gridCenterList[i].x;
//比较最小距离
if(Mathf.Abs(temOffset) < Mathf.Abs(minOffet))
{
minOffet = temOffset;
//格子在中间,反推画布的坐标
snapTargetPos.x = -(gridCenterList[i].x - scrollHalfSize.x);
}
}
snapTargetPos.y = contentRectTrans.anchoredPosition.y;
}
private void UpdateSnapReverse()
{
if(Mathf.Abs(contentRectTrans.anchoredPosition.x - snapTargetPos.x) < 1)
{
contentRectTrans.anchoredPosition = snapTargetPos;
EndSnap();
return;
}
contentRectTrans.anchoredPosition = Vector2.Lerp(contentRectTrans.anchoredPosition,snapTargetPos,snapDecelerate);
}
private void EndSnap()
{
if(snapState == SnapState.None)
return;
scrollRect.StopMovement();
snapState = SnapState.None;
if(isScale)
UpdateScale();
OnScrollEndDrag?.Invoke(curSelectIndex);
}
private void BreakSnap()
{
if(snapState != SnapState.None)
snapState = SnapState.None;
}
#endregion
#region --- Scale ---
int tempIndex;
float tempCenter;
float tempOffset;
float minDistance;
Vector3 tempScale;
Vector2 tempAnPos;
private void UpdateScale()
{
minDistance = float.MaxValue;
tempCenter = Mathf.Abs(contentRectTrans.anchoredPosition.x) + scrollHalfSize.x;
for(int i = 0; i < gridCenterList.Count; i++)
{
if(!gridList[i].gameObject.activeSelf)
continue;
//格子中心到屏幕中心距离
tempOffset = Mathf.Abs(tempCenter - gridCenterList[i].x);
if(tempOffset > scrollHalfSize.x + gridSize.x)
continue;
//计算缩放值
tempScale.x = scaleCurve.Evaluate(tempOffset / scrollHalfSize.x);
tempScale.y = tempScale.x;
tempScale.z = 1;
//修改缩放
gridList[i].localScale = tempScale;
//修改位置(锚点在左上角,保证缩放后格子仍然在中间)
tempAnPos.x = gridCenterList[i].x - gridSize.x * 0.5f * tempScale.x;
tempAnPos.y = gridCenterList[i].y + gridSize.y * (0.5f * tempScale.y - 1);
gridList[i].anchoredPosition = tempAnPos;
//比较最小距离
if(tempOffset < minDistance)
{
minDistance = tempOffset;
tempIndex = i;
}
}
if(curSelectIndex != tempIndex)
{
curSelectIndex = tempIndex;
OnSelectGridChanged?.Invoke(curSelectIndex);
}
}
private void UpdateSelectGrid()
{
minDistance = float.MaxValue;
tempCenter = Mathf.Abs(contentRectTrans.anchoredPosition.x) + scrollHalfSize.x;
for(int i = 0; i < gridCenterList.Count; i++)
{
if(!gridList[i].gameObject.activeSelf)
continue;
//格子中心到屏幕中心距离
tempOffset = Mathf.Abs(tempCenter - gridCenterList[i].x);
if(tempOffset > scrollHalfSize.x + gridSize.x)
continue;
//比较最小距离
if(tempOffset < minDistance)
{
minDistance = tempOffset;
tempIndex = i;
}
}
if(curSelectIndex != tempIndex)
{
curSelectIndex = tempIndex;
OnSelectGridChanged?.Invoke(curSelectIndex);
}
}
#endregion
}
Demo链接:
边栏推荐
- 安信证券属于什么档次 开户安全吗
- The Ministry of human resources and Social Security announced the new construction occupation
- Principle and general steps of SQL injection
- 一图看懂ThreadLocal
- Smart Logistics Park supply chain management system solution: digital intelligent supply chain enables a new supply chain model for the logistics transportation industry
- Which domestic cloud management platform manufacturer is good in 2022? Why?
- C implementation defines a set of intermediate SQL statements that can be executed across libraries
- How can programmers improve the speed of code writing?
- PingCode 性能测试之负载测试实践
- Is it safe for CITIC Securities to open an account online? Is the account opening fee charged
猜你喜欢
How can programmers improve the speed of code writing?
世界环境日 | 周大福用心服务推动减碳环保
智慧物流園區供應鏈管理系統解决方案:數智化供應鏈賦能物流運輸行業供應鏈新模式
Understand Alibaba cloud's secret weapon "dragon architecture" in the article "science popularization talent"
【Go ~ 0到1 】 第六天 文件的读写与创建
Understand asp Net core - Authentication Based on jwtbearer
基于wifi控制的51单片机温度报警器
Chow Tai Fook fulfills the "centenary commitment" and sincerely serves to promote green environmental protection
2022年国内云管平台厂商哪家好?为什么?
Detailed process of DC-2 range construction and penetration practice (DC range Series)
随机推荐
L1-072 scratch lottery
World Environment Day | Chow Tai Fook serves wholeheartedly to promote carbon reduction and environmental protection
FIREBIRD使用经验总结
js中的数组筛选fliter
Solution of dealer collaboration system in building materials industry: empowering enterprises to build core competitiveness
Sequence diagram data modeling and industrial chain analysis
How to decrypt worksheet protection password in Excel file
线程池的使用和原理
Overflow: the combination of auto and Felx
【Go ~ 0到1 】 第六天 文件的读写与创建
Is it safe for Bank of China Securities to open an account online?
基于wifi控制的51单片机温度报警器
51 single chip microcomputer temperature alarm based on WiFi control
Readis configuration and optimization of NoSQL (final chapter)
网页游戏引擎
go-micro教程 — 第二章 go-micro v3 使用Gin、Etcd
世界环境日 | 周大福用心服务推动减碳环保
Go language loop statement (under Lesson 10)
How can programmers improve the speed of code writing?
leetcode:421. The maximum XOR value of two numbers in the array