Skip to content

Instantly share code, notes, and snippets.

@lmartim
Last active March 26, 2025 22:52
Show Gist options
  • Save lmartim/fe5ff3f133c24948838223352b06303f to your computer and use it in GitHub Desktop.
Save lmartim/fe5ff3f133c24948838223352b06303f to your computer and use it in GitHub Desktop.
Auto scroll vertically a ScrollRect when selecting a component outside the viewport
// https://www.reddit.com/r/Unity3D/comments/1epw3u6/need_some_help_with_scroll_view_auto_scroll/
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class ScrollRectAutoScroll : MonoBehaviour
{
[SerializeField] private ScrollRect scrollRect;
[SerializeField] private GameObject cardPrefab;
private RectTransform contentRect;
private List<GameObject> elements;
private void Start()
{
//get the scroll rect components
scrollRect = GetComponent<ScrollRect>();
contentRect = scrollRect.content;
//creates the list of cards, can also be a reference of your deck list
elements = new List<GameObject>();
for (int k = 0; k < 15; k++)
{
GameObject go = Instantiate(cardPrefab, contentRect.transform);
go.GetComponentInChildren<TextMeshProUGUI>().text = k.ToString();
elements.Add(go);
}
//add a listener that invokes BringChildIntoViewVertically every time a card is selected
//the card prefab needs to have a EventTrigger component
foreach (GameObject go in elements)
{
EventTrigger eventTrigger = go.GetComponent<EventTrigger>();
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.Select;
entry.callback.AddListener((eventData) => { BringChildIntoViewVertically(go.GetComponent<RectTransform>()); });
eventTrigger.triggers.Add(entry);
}
scrollRect.horizontalNormalizedPosition = 0;
EventSystem.current.SetSelectedGameObject(elements[0]);
}
public void BringChildIntoViewVertically(RectTransform child)
{
//if vertical scrollbar isn't visible, return
if (!scrollRect.verticalScrollbar.gameObject.activeSelf)
return;
scrollRect.content.ForceUpdateRectTransforms();
scrollRect.viewport.ForceUpdateRectTransforms();
// now takes scaling into account
Vector2 viewportLocalPosition = scrollRect.viewport.localPosition;
Vector2 childLocalPosition = child.localPosition;
Vector2 newContentPosition = new Vector2(
0 - ((viewportLocalPosition.x * scrollRect.viewport.localScale.x) + (childLocalPosition.x * scrollRect.content.localScale.x)),
0 - ((viewportLocalPosition.y * scrollRect.viewport.localScale.y) + (childLocalPosition.y * scrollRect.content.localScale.y))
);
// clamp positions
scrollRect.content.localPosition = newContentPosition;
Rect contentRectInViewport = TransformRectFromTo(scrollRect.content.transform, scrollRect.viewport);
float deltaXMin = contentRectInViewport.xMin - scrollRect.viewport.rect.xMin;
if (deltaXMin > 0) // clamp to <= 0
newContentPosition.x -= deltaXMin;
float deltaXMax = contentRectInViewport.xMax - scrollRect.viewport.rect.xMax;
if (deltaXMax < 0) // clamp to >= 0
newContentPosition.x -= deltaXMax;
float deltaYMin = contentRectInViewport.yMin - scrollRect.viewport.rect.yMin;
if (deltaYMin > 0) // clamp to <= 0
newContentPosition.y -= deltaYMin;
float deltaYMax = contentRectInViewport.yMax - scrollRect.viewport.rect.yMax;
if (deltaYMax < 0) // clamp to >= 0
newContentPosition.y -= deltaYMax;
// apply final position
scrollRect.content.localPosition = newContentPosition;
scrollRect.content.ForceUpdateRectTransforms();
}
public static Rect TransformRectFromTo(Transform from, Transform to)
{
RectTransform fromRectTrans = from.GetComponent<RectTransform>();
RectTransform toRectTrans = to.GetComponent<RectTransform>();
if (fromRectTrans != null && toRectTrans != null)
{
Vector3[] fromWorldCorners = new Vector3[4];
Vector3[] toLocalCorners = new Vector3[4];
Matrix4x4 toLocal = to.worldToLocalMatrix;
fromRectTrans.GetWorldCorners(fromWorldCorners);
for (int i = 0; i < 4; i++)
toLocalCorners[i] = toLocal.MultiplyPoint3x4(fromWorldCorners[i]);
return new Rect(toLocalCorners[0].x, toLocalCorners[0].y, toLocalCorners[2].x - toLocalCorners[1].x,
toLocalCorners[1].y - toLocalCorners[0].y);
}
return default(Rect);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment