当前位置:网站首页>【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链接:
边栏推荐
- 电子元器件B2B商城系统开发:赋能企业构建进销存标准化流程实例
- System. Currenttimemillis() and system Nanotime (), which is faster? Don't use it wrong!
- NoSQL之readis配置与优化(终章)
- 网页游戏引擎
- Blood spitting finishing nanny level series tutorial - play Fiddler bag grabbing tutorial (2) - first meet fiddler, let you have a rational understanding
- How to choose one plus 10 pro and iPhone 13?
- Which domestic cloud management platform manufacturer is good in 2022? Why?
- Solution of dealer collaboration system in building materials industry: empowering enterprises to build core competitiveness
- Is it safe for Bank of China Securities to open an account online?
- js中的数组筛选fliter
猜你喜欢

The test experience "tortured" by the PMP test is worth your review

第十八届IET交直流输电国际会议(ACDC2022)于线上成功举办

~89 deformation translation

It's too convenient. You can complete the code release and approval by nailing it!

太方便了,钉钉上就可完成代码发布审批啦!

程序员怎么才能提高代码编写速度?

利用win10计划任务程序定时自动运行jar包

Transformer中position encoding实践

Years of training, towards Kata 3.0! Enter the safe container experience out of the box | dragon lizard Technology

MVC模式和三层架构
随机推荐
容器环境minor gc异常频繁分析
How to "use" Perl modules in directories that are not in @inc- How do I 'use' a Perl module in a directory not in @INC?
[acwing] 58 weeks 4489 Longest subsequence
【Go ~ 0到1 】 第六天 文件的读写与创建
Overflow: the combination of auto and Felx
Object.keys()的用法
Embedded software architecture design - function call
Congratulations to Mr. Zhang Pengfei, chief data scientist of artefact, for winning the campaign Asia tech MVP 2022
Array filter fliter in JS
[Acwing] 58周赛 4489. 最长子序列
"Cannot initialize Photoshop because the temporary storage disk is full" graphic solution
被PMP考试“折磨”出来的考试心得,值得你一览
. Net applications consider x64 generation
Configuration instance of Oracle listener server and client
Task state rollback and data blocking tasks based on check point mechanism
如何实现一个延时队列 ?
Sql实现Split
智慧物流園區供應鏈管理系統解决方案:數智化供應鏈賦能物流運輸行業供應鏈新模式
Inside and outside: flow chart drawing elementary: six common mistakes
如何为ONgDB核心项目源码做贡献