I have been looking for a good solution to get the position of any element in a Scroll View, to Smoothly scroll to that element.
My current implementation is:
private void SmoothScrollTo(ScrollView scrollView, Button button)
{
float startPosition = scrollView.verticalScroller.value;
scrollView.ScrollTo(button);
float endPosition = scrollView.verticalScroller.value;
scrollView.verticalScroller.value = startPosition;
if (startPosition == endPosition) return;
StartCoroutine(SmoothScrollToButton(scrollView, endPosition));
}
private IEnumerator SmoothScrollToButton(ScrollView scrollView, float endPosition)
{
float smoothTime = 0.3f;
float elapsedTime = 0f;
float startPosition = scrollView.verticalScroller.value;
while (elapsedTime < smoothTime)
{
float t = elapsedTime / smoothTime;
float smoothPosition = Mathf.SmoothStep(startPosition, endPosition, t);
scrollView.verticalScroller.value = smoothPosition;
yield return null;
elapsedTime += Time.deltaTime;
}
scrollView.verticalScroller.value = endPosition;
}
This does work, however, I’m looking to be able to centre the element in the scroll view and ScrollTo(); only brings it into frame.
Does anyone have any ideas as to how we can get the position of an element inside a scroll view?
Or perhaps have I missed an existing solution?
It would also be really nice to have some sort of scrollview.GetPosition(element) that returns the xy positions of the element relative to scrollView.verticalScroller.value. That way we can just use the elements information to offset to get center if needed.
The ScrollView’s scrollOffset is basically the position in the content container. So you just need to position of the element in relation to the scrollView.contentContainer
.
You can get the position of the element in that space using WorldToLocal
or ChangeCoordinatesTo
for example. (see Unity - Manual: Coordinate and position systems).
Thanks for the reply!
Unfortunately, after a bit of experimentation, I wasn’t able to successfully use WorldToLocal
or ChangeCoordinatesTo
. I couldn’t seem to find a way to get the correct positions, but that’s likely something I’m not seeing yet.
However, in researching them, I came across a way to get the position data from VisualElement.layout.position
. In the end, I went with this solution:
private void SmoothScrollTo(ScrollView scrollView, VisualElement element)
{
float yPos = element.layout.position.y;
float height = element.layout.height/2;
float postion = yPos - height;
Debug.Log($"Button Position: {yPos}, Button Height: {height}, Final Postion: {postion}");
StartCoroutine(SmoothScrollToElement(scrollView, postion));
}
private IEnumerator SmoothScrollToElement(ScrollView scrollView, float endPosition)
{
float smoothTime = 0.3f;
float elapsedTime = 0f;
float startPosition = scrollView.verticalScroller.value;
while (elapsedTime < smoothTime)
{
float t = elapsedTime / smoothTime;
float smoothPosition = Mathf.SmoothStep(startPosition, endPosition, t);
scrollView.verticalScroller.value = smoothPosition;
yield return null;
elapsedTime += Time.deltaTime;
}
scrollView.verticalScroller.value = endPosition;
//Will show if element is next to start or end of scrollview
if (endPosition != scrollView.verticalScroller.value)
Debug.Log($"End Position: {scrollView.verticalScroller.value}");
}
This solution allows me to scroll to the chosen element in the ScrollView and have the element centred on the y-axis in the viewport.
Note: The top and bottom elements won’t centre (fine for my purposes) as verticalScroller.value
will be clamped between verticalScroller.lowValue
and verticalScroller.highValue
.
TLDR: Use VisualElement.layout.position
to get the position of the child element relative to to the ScrollView.