Why is my Scrollrect content jittering?

I’ve got a 2d UI in Screen Space - Camera containing:
ScrollRect > Viewport > ContentSizeFitter > Text

The text is short so it fits without triggering a scroll bar. Everything SHOULD be at Y = 0. Instead, the ContentSizeFitter’s Y is changing to values between -0.003 ~ 0.003 at every frame. It makes the text jitter up and down.

Same issue on a different ScrollRect containing a long grid of images, even after scrolling down.

I noticed this after upgrading to 2017.3.1f1 but not sure if this is a Unity bug or mine. My camera follows a NavMeshAgent around a 3D scene, but movement stops when the menu is showing.

Any ideas?

I’m guessing this is a Unity bug.

The cause: ScrollRect.LateUpdate calls SetContentAnchoredPosition every frame with values between -0.003 ~ 0.003 when the scrollbar is hidden, even though velocity is zero.

The solution: subclass ScrollRect and ignore SetContentAnchoredPosition if the scrollbar is hidden.

Here is my subclass (supports vertical scrollbars only):

using UnityEngine;
using UnityEngine.UI;

public class NWScrollRect : ScrollRect {
	/// <summary>
	/// ScrollRect.LateUpdate calls this function with very tiny values every frame,
	/// only if scrolling is not needed and even when velocity is zero.
	/// This makes text jitter. Check before setting position.
	/// </summary>
	protected override void SetContentAnchoredPosition(Vector2 position) {
		if (Application.isPlaying && verticalScrollbar != null && !verticalScrollbar.IsActive()) {
			if (content.anchoredPosition != Vector2.zero) {
				position = Vector2.zero;
			} else {
				return;
			}
		}

		// fires every frame in editor, but without this text jitters
		if (position != Vector2.zero && Approximately(content.anchoredPosition, position) 
			&& Approximately(position, Vector2.zero)) {
			position = Vector2.zero;
		}
		
		base.SetContentAnchoredPosition(position);
	}

	/// <summary>
	/// Called when scrolling would occur.
	/// Prevent setting when vertical scrollbar is disabled and scrolling is not needed to prevent jittering.
	/// </summary>
	protected override void SetNormalizedPosition(float value, int axis) {
		if (Application.isPlaying && verticalScrollbar != null && !verticalScrollbar.IsActive()) {
			return;
		}
		base.SetNormalizedPosition(value, axis);
	}
	
	private static bool Approximately(Vector2 vec1, Vector2 vec2, float threshold = 0.01f) {
		return ((vec1.x < vec2.x) ? (vec2.x - vec1.x) : (vec1.x - vec2.x)) <= threshold
			&& ((vec1.y < vec2.y) ? (vec2.y - vec1.y) : (vec1.y - vec2.y)) <= threshold;
	}
}