当前位置:网站首页>[unity ugui] scrollrect dynamically scales the grid size and automatically locates the middle grid
[unity ugui] scrollrect dynamically scales the grid size and automatically locates the middle grid
2022-07-04 17:30:00 【InfoQ】

Instructions :


Effect demonstration :




Realization principle :

Implementation code :
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));
// Initialize invariant fields
private ScrollRect scrollRect;
private RectTransform contentRectTrans;
private Vector2 scrollHalfSize; // List size
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;
// Dynamically changing fields
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(" Currently, only horizontal lists from left to right are supported ");
contentRectTrans = scrollRect.content;
if(contentRectTrans.pivot.x > 0)
Debug.LogError(" Currently, only horizontal lists from left to right are supported ");
//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;
// The first grid coordinate
Vector2 gridInitPos = gridList[0].anchoredPosition;
gridInitPos.x = scrollHalfSize.x - gridSize.x * 0.5f;
// Grid spacing
Vector2 gridOffset = Vector2.zero;
gridOffset.x = gridSize.x + gridSpace;
// Calculate the canvas size
Vector2 contentSize = contentRectTrans.rect.size;
contentSize.x = gridSize.x * gridList.Count + gridSpace * (gridList.Count - 1) + scrollHalfSize.x * 2 - gridSize.x;
// Set the canvas size
contentRectTrans.sizeDelta = contentSize;
contentRectTrans.anchoredPosition = new Vector2(0,contentRectTrans.anchoredPosition.y);
// Set the coordinates of each grid
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)
{
// reverse
StartSnapReverse();
return;
}
}
private void StartSnapReverse()
{
snapState = SnapState.Reverse;
scrollRect.StopMovement();
// Canvas coordinates of the center of the current screen
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;
// Grid center coordinates
temOffset = centerPos - gridCenterList[i].x;
// Compare the minimum distance
if(Mathf.Abs(temOffset) < Mathf.Abs(minOffet))
{
minOffet = temOffset;
// The grid is in the middle , Reverses the coordinates of the canvas
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;
// Distance from grid center to screen center
tempOffset = Mathf.Abs(tempCenter - gridCenterList[i].x);
if(tempOffset > scrollHalfSize.x + gridSize.x)
continue;
// Calculate the scaling value
tempScale.x = scaleCurve.Evaluate(tempOffset / scrollHalfSize.x);
tempScale.y = tempScale.x;
tempScale.z = 1;
// Modify zoom
gridList[i].localScale = tempScale;
// Change location ( The anchor is in the upper left corner , Make sure the grid is still in the middle after scaling )
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;
// Compare the minimum distance
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;
// Distance from grid center to screen center
tempOffset = Mathf.Abs(tempCenter - gridCenterList[i].x);
if(tempOffset > scrollHalfSize.x + gridSize.x)
continue;
// Compare the minimum distance
if(tempOffset < minDistance)
{
minDistance = tempOffset;
tempIndex = i;
}
}
if(curSelectIndex != tempIndex)
{
curSelectIndex = tempIndex;
OnSelectGridChanged?.Invoke(curSelectIndex);
}
}
#endregion
}
Demo link :
边栏推荐
- kaili不能输入中文怎么办???
- Congratulations to Mr. Zhang Pengfei, chief data scientist of artefact, for winning the campaign Asia tech MVP 2022
- The 18th IET AC / DC transmission International Conference (acdc2022) was successfully held online
- 世界环境日 | 周大福用心服务推动减碳环保
- Why do you say that the maximum single table of MySQL database is 20million? Based on what?
- What grade does Anxin securities belong to? Is it safe to open an account
- Overflow: the combination of auto and Felx
- Yanwen logistics plans to be listed on Shenzhen Stock Exchange: it is mainly engaged in international express business, and its gross profit margin is far lower than the industry level
- Rebalance operation in spark and its difference from repartition operation
- 整理混乱的头文件,我用include what you use
猜你喜欢
Years of training, towards Kata 3.0! Enter the safe container experience out of the box | dragon lizard Technology
ble HCI 流控机制
Congratulations to Mr. Zhang Pengfei, chief data scientist of artefact, for winning the campaign Asia tech MVP 2022
The company needs to be monitored. How do ZABBIX and Prometheus choose? That's the right choice!
MVC模式和三层架构
How can programmers improve the speed of code writing?
Readis configuration and optimization of NoSQL (final chapter)
居家打工年入800多万,一共五份全职工作,他还有时间打游戏
建筑建材行业经销商协同系统解决方案:赋能企业构建核心竞争力
如何实现一个延时队列 ?
随机推荐
新享科技发布小程序UniPro小优 满足客户移动办公场景
"Cannot initialize Photoshop because the temporary storage disk is full" graphic solution
离线、开源版的 Notion—— 笔记软件Anytype 综合评测
With an annual income of more than 8 million, he has five full-time jobs. He still has time to play games
Is it safe for Bank of China Securities to open an account online?
The Ministry of human resources and Social Security announced the new construction occupation
Linear time sequencing
NFT流动性市场安全问题频发—NFT交易平台Quixotic被黑事件分析
Blood spitting finishing nanny level series tutorial - play Fiddler bag grabbing tutorial (2) - first meet fiddler, let you have a rational understanding
What grade does Anxin securities belong to? Is it safe to open an account
Visual studio 2019 (localdb) mssqllocaldb SQL Server 2014 database version is 852 and cannot be opened. This server supports 782
长城证券安全不 证券开户
egg. JS learning notes
Offline and open source version of notation -- comprehensive evaluation of note taking software anytype
新的职业已经出现,怎么能够停滞不前 ,人社部公布建筑新职业
Solution of dealer collaboration system in building materials industry: empowering enterprises to build core competitiveness
Leetcode list summary
VMware Tools和open-vm-tools的安装与使用:解决虚拟机不全屏和无法传输文件的问题
Web game engine
Display opencv drawn pictures on MFC picture control control