当前位置:网站首页>[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

Realization effect :ScrollRect, Grid dynamic zoom size , The sliding end automatically locates the middle grid

null

Instructions :

Currently, only horizontal lists from left to right are supported , Need to set up ScrollRect Slide laterally only ,ScrollViewExtras Hang up ScrollRect Component node ,ScrollRect Grid anchor of 、 The center point is set in the upper left corner ,Content, Set it up Y coordinate , Automatically modify at run time X coordinate .

GridSpace: Grid spacing
IsSnap: Whether to enable automatic positioning
IsScale: Whether to turn on auto zoom
ScaleCueve: Scale curve

null
null

Effect demonstration :

Turn off automatic positioning , Turn off autoscale :

null
Turn on automatic positioning , Turn off autoscale :

null
Turn off automatic positioning , Turn on autoscale :

null
Turn on automatic positioning , Turn on autoscale :

null

Realization principle :

Dynamic zoom function :Transform When the change , Calculate the offset of all grids in the visible area of the list from the center of the list , Scale the grid according to the offset . The automatic positioning function is realized : Initialize the calculation of the position of each grid , After sliding , Calculate the offset of each grid from the center of the list , The grid with the smallest offset is the grid in the middle of the list , And then automatically move it to the center of the list , Achieve positioning .

null

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(&quot; Currently, only horizontal lists from left to right are supported &quot;);
 
 contentRectTrans = scrollRect.content;
 if(contentRectTrans.pivot.x > 0)
 Debug.LogError(&quot; Currently, only horizontal lists from left to right are supported &quot;);
 
 //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 :

https://download.csdn.net/download/qq_39108767/85827448
https://download.csdn.net/download/qq_39108767/85827448
原网站

版权声明
本文为[InfoQ]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/185/202207041532498535.html