Skip to content

Commit eaa0d7f

Browse files
authored
Merge pull request PCL-Community#123 from MinecraftYJQ/main
feat: 添加页面平滑滚动
2 parents b88eb08 + fe4c607 commit eaa0d7f

16 files changed

+3252
-2
lines changed

PCL.CompositionScroll/CompositionScrollContentPresenter.cs

Lines changed: 1043 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Avalonia.Rendering.Composition;
2+
3+
namespace CompositionScroll.Interactions
4+
{
5+
public static class Factory
6+
{
7+
public static InteractionTracker CreateInteractionTracker(this Compositor compositor)
8+
{
9+
return compositor.CreateInteractionTracker(null);
10+
}
11+
12+
public static InteractionTracker CreateInteractionTracker(this Compositor compositor, IInteractionTrackerOwner owner)
13+
{
14+
var tracker = new InteractionTracker(compositor, owner);
15+
tracker.Init();
16+
return tracker;
17+
}
18+
19+
public static InteractionTrackerInertiaRestingValue CreateInteractionTrackerInertiaRestingValue(this Compositor compositor)
20+
=> InteractionTrackerInertiaRestingValue.Create(compositor);
21+
}
22+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using Avalonia;
2+
3+
namespace CompositionScroll.Interactions
4+
{
5+
public interface IInteractionTrackerOwner
6+
{
7+
void ValuesChanged(InteractionTracker sender, InteractionTrackerValuesChangedArgs args);
8+
9+
void IdleStateEntered(InteractionTracker sender, InteractionTrackerIdleStateEnteredArgs args);
10+
11+
void InertiaStateEntered(InteractionTracker sender, InteractionTrackerInertiaStateEnteredArgs args);
12+
13+
void InteractingStateEntered(InteractionTracker sender, InteractionTrackerInteractingStateEnteredArgs args);
14+
}
15+
16+
public sealed class InteractionTrackerValuesChangedArgs
17+
{
18+
internal InteractionTrackerValuesChangedArgs(Vector3D position, long requestId)
19+
{
20+
Position = position;
21+
RequestId = requestId;
22+
}
23+
24+
public Vector3D Position { get; }
25+
26+
public long RequestId { get; }
27+
}
28+
29+
public sealed class InteractionTrackerIdleStateEnteredArgs
30+
{
31+
internal InteractionTrackerIdleStateEnteredArgs(long requestId)
32+
{
33+
RequestId = requestId;
34+
}
35+
36+
public long RequestId { get; }
37+
}
38+
39+
public sealed class InteractionTrackerInertiaStateEnteredArgs
40+
{
41+
internal InteractionTrackerInertiaStateEnteredArgs(bool fromImpulse, Vector3D? modifiedRestingPosition, Vector3D naturalRestingPosition, Vector3D positionVelocity, long requestId)
42+
{
43+
IsInertiaFromImpulse = fromImpulse;
44+
ModifiedRestingPosition = modifiedRestingPosition;
45+
NaturalRestingPosition = naturalRestingPosition;
46+
PositionVelocityInPixelsPerSecond = positionVelocity;
47+
RequestId = requestId;
48+
}
49+
50+
public bool IsInertiaFromImpulse { get; }
51+
52+
public Vector3D? ModifiedRestingPosition { get; }
53+
54+
public Vector3D NaturalRestingPosition { get; }
55+
56+
public Vector3D PositionVelocityInPixelsPerSecond { get; }
57+
58+
public long RequestId { get; }
59+
}
60+
61+
public class InteractionTrackerInteractingStateEnteredArgs
62+
{
63+
internal InteractionTrackerInteractingStateEnteredArgs(long requestId)
64+
{
65+
RequestId = requestId;
66+
}
67+
68+
public long RequestId { get; }
69+
}
70+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
using Avalonia;
2+
using Avalonia.Input;
3+
using Avalonia.Input.GestureRecognizers;
4+
using System;
5+
6+
namespace CompositionScroll.Interactions
7+
{
8+
public sealed class InteractionSource : GestureRecognizer
9+
{
10+
private static int Id = 1;
11+
private static int GetId() => Id++;
12+
13+
private int _interactionId;
14+
private IPointer _tracking;
15+
private Visual _rootTarget;
16+
private Point _pointerPressedPoint;
17+
private VelocityTracker _velocityTracker;
18+
private bool _scrolling;
19+
20+
public InteractionSource(IInputElement target)
21+
{
22+
target.PointerWheelChanged += Target_PointerWheelChanged;
23+
}
24+
25+
private InteractionTracker Tracker { get; set; }
26+
27+
public bool CanHorizontallyScroll { get; set; } = false;
28+
29+
public bool CanVerticallyScroll { get; set; } = true;
30+
31+
public bool IsScrollInertiaEnabled { get; set; } = true;
32+
33+
public ScrollFeaturesEnum ScrollFeatures { get; set; } = ScrollFeaturesEnum.None;
34+
35+
public double ScrollStartDistance { get; set; } = 10;
36+
37+
private bool CanAny(ScrollFeaturesEnum feature) => (feature & ScrollFeatures) != ScrollFeaturesEnum.None;
38+
39+
private void Target_PointerWheelChanged(object sender, PointerWheelEventArgs e)
40+
{
41+
if (Tracker == null)
42+
return;
43+
44+
var delta = e.Delta;
45+
46+
if (CanAny(ScrollFeaturesEnum.WheelSwapDirections))
47+
{
48+
delta = new Vector(delta.Y, delta.X);
49+
}
50+
51+
if (e.KeyModifiers == KeyModifiers.Shift)
52+
{
53+
delta = new Vector(delta.Y, delta.X);
54+
}
55+
56+
delta = FitVector(delta, CanHorizontallyScroll, CanVerticallyScroll);
57+
delta = delta.Negate();
58+
59+
delta = delta * 50;
60+
61+
Tracker.AnimatePositionBy(new Vector3D(delta.X, delta.Y, 0));
62+
e.Handled = true;
63+
}
64+
65+
protected override void PointerPressed(PointerPressedEventArgs e)
66+
{
67+
if (e.Pointer.Type == PointerType.Mouse && !CanAny(ScrollFeaturesEnum.MousePressedScroll))
68+
return;
69+
70+
EndGesture();
71+
_tracking = e.Pointer;
72+
_interactionId = GetId();
73+
_rootTarget = (Visual)((Target as Visual)?.VisualRoot);
74+
_pointerPressedPoint = e.GetPosition(_rootTarget);
75+
_velocityTracker = new VelocityTracker();
76+
_velocityTracker.AddPosition(TimeSpan.FromMilliseconds(e.Timestamp), default);
77+
BeginUserInteraction();
78+
}
79+
80+
protected override void PointerMoved(PointerEventArgs e)
81+
{
82+
if (e.Pointer != _tracking)
83+
return;
84+
85+
var rootPoint = e.GetPosition(_rootTarget);
86+
if (!_scrolling)
87+
{
88+
if (CanHorizontallyScroll && Math.Abs(_pointerPressedPoint.X - rootPoint.X) > ScrollStartDistance)
89+
_scrolling = true;
90+
if (CanVerticallyScroll && Math.Abs(_pointerPressedPoint.Y - rootPoint.Y) > ScrollStartDistance)
91+
_scrolling = true;
92+
93+
if (_scrolling)
94+
{
95+
Capture(_tracking);
96+
}
97+
}
98+
99+
if (_scrolling)
100+
{
101+
Vector delta = _pointerPressedPoint - rootPoint;
102+
delta = FitVector(delta, CanHorizontallyScroll, CanVerticallyScroll);
103+
104+
_velocityTracker?.AddPosition(TimeSpan.FromMilliseconds(e.Timestamp), delta);
105+
UserInteraction(delta);
106+
e.Handled = true;
107+
}
108+
}
109+
110+
protected override void PointerReleased(PointerReleasedEventArgs e)
111+
{
112+
if (e.Pointer != _tracking)
113+
return;
114+
115+
var inertia = _velocityTracker?.GetFlingVelocity().PixelsPerSecond ?? Vector.Zero;
116+
inertia = FitVector(inertia, CanHorizontallyScroll, CanVerticallyScroll);
117+
118+
if (_scrolling &&
119+
IsScrollInertiaEnabled &&
120+
(e.Pointer.Type != PointerType.Mouse || CanAny(ScrollFeaturesEnum.MousePressedScrollEnertia)))
121+
{
122+
EndGesture(inertia);
123+
}
124+
else
125+
{
126+
EndGesture();
127+
}
128+
}
129+
130+
protected override void PointerCaptureLost(IPointer pointer)
131+
{
132+
if (pointer != _tracking)
133+
return;
134+
135+
EndGesture();
136+
}
137+
138+
private void EndGesture(Vector? inertia = null)
139+
{
140+
EndUserInteraction(inertia);
141+
_tracking = null;
142+
if (_scrolling)
143+
{
144+
_scrolling = false;
145+
_interactionId = 0;
146+
_rootTarget = null;
147+
}
148+
}
149+
150+
private static Vector FitVector(Vector vector, bool canHoriz, bool canVer)
151+
{
152+
if (!canHoriz)
153+
vector = new Point(0, vector.Y);
154+
if (!canVer)
155+
vector = new Point(vector.X, 0);
156+
157+
return vector;
158+
}
159+
160+
private void BeginUserInteraction()
161+
{
162+
Tracker.InteractionStart(_interactionId);
163+
}
164+
165+
private void UserInteraction(Vector delta)
166+
{
167+
var delta3D = new Vector3D(delta.X, delta.Y, 0);
168+
Tracker.InteractionMove(_interactionId, delta3D);
169+
}
170+
171+
private void EndUserInteraction(Vector? inertia = null)
172+
{
173+
if (inertia == null)
174+
Tracker.InteractionEnd(_interactionId);
175+
else
176+
{
177+
var inertia3D = new Vector3D(inertia.Value.X, inertia.Value.Y, 0);
178+
Tracker.InteractionEndWithInertia(_interactionId, inertia3D);
179+
}
180+
}
181+
182+
internal void SetInteractionTracker(InteractionTracker tracker)
183+
{
184+
Tracker = tracker;
185+
}
186+
}
187+
}

0 commit comments

Comments
 (0)