当前位置:网站首页>[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 :
边栏推荐
- Is it safe for Bank of China Securities to open an account online?
- Solution of dealer collaboration system in building materials industry: empowering enterprises to build core competitiveness
- Go language loop statement (under Lesson 10)
- kaili不能输入中文怎么办???
- 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
- Datakit -- the real unified observability agent
- 上网成瘾改变大脑结构:语言功能受影响,让人话都说不利索
- wuzhicms代码审计
- Why do you say that the maximum single table of MySQL database is 20million? Based on what?
- Blood spitting finishing nanny level series tutorial - play Fiddler bag grabbing tutorial (2) - first meet fiddler, let you have a rational understanding
猜你喜欢

整理混乱的头文件,我用include what you use

GO开发:如何利用Go单例模式保障流媒体高并发的安全性?

Hidden corners of coder Edition: five things that developers hate most

To sort out messy header files, I use include what you use

Go development: how to use go singleton mode to ensure the security of high concurrency of streaming media?

leetcode:421. 数组中两个数的最大异或值

一加10 Pro和iPhone 13怎么选?

La 18e Conférence internationale de l'IET sur le transport d'électricité en courant alternatif et en courant continu (acdc2022) s'est tenue avec succès en ligne.

智慧物流园区供应链管理系统解决方案:数智化供应链赋能物流运输行业供应链新模式

NFT liquidity market security issues occur frequently - Analysis of the black incident of NFT trading platform quixotic
随机推荐
kaili不能输入中文怎么办???
智慧物流園區供應鏈管理系統解决方案:數智化供應鏈賦能物流運輸行業供應鏈新模式
Using win10 scheduling task program to automatically run jar package at fixed time
上网成瘾改变大脑结构:语言功能受影响,让人话都说不利索
世界环境日 | 周大福用心服务推动减碳环保
斑马识别成狗,AI犯错的原因被斯坦福找到了丨开源
The winning rate against people is 84%, and deepmind AI has reached the level of human experts in army chess for the first time
Redis 的内存淘汰策略和过期删除策略的区别
手里10万元存款买什么理财产品收益最高?
S2b2b solution for lighting industry: efficiently enable the industrial supply chain and improve the economic benefits of enterprises
整理混乱的头文件,我用include what you use
Years of training, towards Kata 3.0! Enter the safe container experience out of the box | dragon lizard Technology
To sort out messy header files, I use include what you use
Unity interview questions (continuously updated)
go-micro教程 — 第二章 go-micro v3 使用Gin、Etcd
NFT流动性市场安全问题频发—NFT交易平台Quixotic被黑事件分析
Summary of tx.origin security issues
照明行业S2B2B解决方案:高效赋能产业供应链,提升企业经济效益
R language plot visualization: plot visualizes overlapping histograms and uses geom at the top edge of the histogram_ The rug function adds marginal rug plots
The company needs to be monitored. How do ZABBIX and Prometheus choose? That's the right choice!