in src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs [963:1046]
private Vector SnapOffset(Vector offset, Vector direction = default, bool snapToNext = false)
{
var scrollable = GetScrollSnapPointsInfo(Content);
if (scrollable is null || (VerticalSnapPointsType == SnapPointsType.None && HorizontalSnapPointsType == SnapPointsType.None))
return offset;
var diff = GetAlignmentDiff();
if (VerticalSnapPointsType != SnapPointsType.None && (_areVerticalSnapPointsRegular || _verticalSnapPoints?.Count > 0) && (!snapToNext || snapToNext && direction.Y != 0))
{
var estimatedOffset = new Vector(offset.X, offset.Y + diff.Y);
double previousSnapPoint = 0, nextSnapPoint = 0, midPoint = 0;
if (_areVerticalSnapPointsRegular)
{
previousSnapPoint = (int)(estimatedOffset.Y / _verticalSnapPoint) * _verticalSnapPoint + _verticalSnapPointOffset;
nextSnapPoint = previousSnapPoint + _verticalSnapPoint;
midPoint = (previousSnapPoint + nextSnapPoint) / 2;
}
else if (_verticalSnapPoints?.Count > 0)
{
(previousSnapPoint, nextSnapPoint) = FindNearestSnapPoint(_verticalSnapPoints, estimatedOffset.Y);
midPoint = (previousSnapPoint + nextSnapPoint) / 2;
}
var nearestSnapPoint = snapToNext ? (direction.Y > 0 ? previousSnapPoint : nextSnapPoint) :
estimatedOffset.Y < midPoint ? previousSnapPoint : nextSnapPoint;
offset = new Vector(offset.X, nearestSnapPoint - diff.Y);
}
if (HorizontalSnapPointsType != SnapPointsType.None && (_areHorizontalSnapPointsRegular || _horizontalSnapPoints?.Count > 0) && (!snapToNext || snapToNext && direction.X != 0))
{
var estimatedOffset = new Vector(offset.X + diff.X, offset.Y);
double previousSnapPoint = 0, nextSnapPoint = 0, midPoint = 0;
if (_areHorizontalSnapPointsRegular)
{
previousSnapPoint = (int)(estimatedOffset.X / _horizontalSnapPoint) * _horizontalSnapPoint + _horizontalSnapPointOffset;
nextSnapPoint = previousSnapPoint + _horizontalSnapPoint;
midPoint = (previousSnapPoint + nextSnapPoint) / 2;
}
else if (_horizontalSnapPoints?.Count > 0)
{
(previousSnapPoint, nextSnapPoint) = FindNearestSnapPoint(_horizontalSnapPoints, estimatedOffset.X);
midPoint = (previousSnapPoint + nextSnapPoint) / 2;
}
var nearestSnapPoint = snapToNext ? (direction.X > 0 ? previousSnapPoint : nextSnapPoint) :
estimatedOffset.X < midPoint ? previousSnapPoint : nextSnapPoint;
offset = new Vector(nearestSnapPoint - diff.X, offset.Y);
}
Vector GetAlignmentDiff()
{
var vector = default(Vector);
switch (VerticalSnapPointsAlignment)
{
case SnapPointsAlignment.Center:
vector += new Vector(0, Viewport.Height / 2);
break;
case SnapPointsAlignment.Far:
vector += new Vector(0, Viewport.Height);
break;
}
switch (HorizontalSnapPointsAlignment)
{
case SnapPointsAlignment.Center:
vector += new Vector(Viewport.Width / 2, 0);
break;
case SnapPointsAlignment.Far:
vector += new Vector(Viewport.Width, 0);
break;
}
return vector;
}
return offset;
}