feat: add animation during auto scrolling (#1153)

This commit is contained in:
Lucas 2025-11-06 12:40:37 +08:00 • committed by GitHub
commit 5c4a6f904c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 37 additions and 9 deletions

View file

@ -20,6 +20,7 @@ class AutoScroller extends EdgeDraggingAutoScroller
super.velocityScalar = _kDefaultAutoScrollVelocityScalar,
super.minimumAutoScrollDelta = _kDefaultMinAutoScrollDelta,
super.maxAutoScrollDelta = _kDefaultMaxAutoScrollDelta,
super.animationDuration,
});
static const double _kDefaultAutoScrollVelocityScalar = 7;
@ -28,6 +29,8 @@ class AutoScroller extends EdgeDraggingAutoScroller
Offset? lastOffset;
Duration? lastDuration;
double? lastEdgeOffset;
AxisDirection? lastDirection;
@override
void startAutoScroll(
@ -38,9 +41,12 @@ class AutoScroller extends EdgeDraggingAutoScroller
}) {
lastOffset = offset;
lastDuration = duration;
lastEdgeOffset = edgeOffset;
lastDirection = direction;
if (direction != null && direction == AxisDirection.up) {
return startAutoScrollIfNecessary(
offset & Size(1, edgeOffset),
duration: duration,
);
}
@ -52,6 +58,7 @@ class AutoScroller extends EdgeDraggingAutoScroller
startAutoScrollIfNecessary(
dragTarget,
duration: duration,
);
}
@ -59,6 +66,8 @@ class AutoScroller extends EdgeDraggingAutoScroller
void stopAutoScroll() {
lastOffset = null;
lastDuration = null;
lastEdgeOffset = null;
lastDirection = null;
super.stopAutoScroll();
}
@ -66,6 +75,8 @@ class AutoScroller extends EdgeDraggingAutoScroller
if (lastOffset != null) {
startAutoScroll(
lastOffset!,
edgeOffset: lastEdgeOffset ?? 200,
direction: lastDirection,
duration: lastDuration,
);
}

View file

@ -101,6 +101,14 @@ class _ScrollServiceWidgetState extends State<ScrollServiceWidget>
AxisDirection? direction;
final dynamic dragMode =
editorState.selectionExtraInfo?['selection_drag_mode'];
// For desktop: if auto-scroller is already scrolling (from drag-to-select),
// don't override it here. The desktop_selection_service handles drag scrolling.
if (PlatformExtension.isDesktopOrWeb &&
(editorState.autoScroller?.scrolling ?? false)) {
return;
}
switch (dragMode?.toString()) {
case 'MobileSelectionDragMode.leftSelectionHandle':
targetRect = selectionRects.first;

View file

@ -345,7 +345,8 @@ class _DesktopSelectionServiceWidgetState
editorState.service.scrollService?.startAutoScroll(
_lastPanOffset!,
edgeOffset: 80,
edgeOffset: 200,
duration: const Duration(milliseconds: 2),
);
}

View file

@ -636,9 +636,10 @@ class EditorState {
late AutoScroller scroller;
scroller = AutoScroller(
scrollableState,
velocityScalar: isDesktopOrWeb ? 0.125 : 0.02,
velocityScalar: isDesktopOrWeb ? 0.15 : 0.02,
minimumAutoScrollDelta: isDesktopOrWeb ? 0.07 : 0.004,
maxAutoScrollDelta: isDesktopOrWeb ? 2.75 : 0.053,
maxAutoScrollDelta: isDesktopOrWeb ? 3.5 : 0.053,
animationDuration: Duration.zero,
onScrollViewScrolled: () {
_notifyScrollViewScrolledListeners();
if (!isDesktopOrWeb) {

View file

@ -150,10 +150,13 @@ class EdgeDraggingAutoScroller {
required this.velocityScalar,
double minimumAutoScrollDelta = 1.0,
double maxAutoScrollDelta = 20.0,
Duration? animationDuration,
}) : assert(minimumAutoScrollDelta >= 0),
assert(maxAutoScrollDelta >= minimumAutoScrollDelta),
_minimumAutoScrollDelta = minimumAutoScrollDelta,
_maxAutoScrollDelta = maxAutoScrollDelta;
_maxAutoScrollDelta = maxAutoScrollDelta,
_animationDuration =
animationDuration ?? const Duration(milliseconds: 5);
/// The [Scrollable] this auto scroller is scrolling.
final ScrollableState scrollable;
@ -180,6 +183,8 @@ class EdgeDraggingAutoScroller {
/// moving rather than treat it as too small to scroll.
final double _minimumAutoScrollDelta;
final double _maxAutoScrollDelta;
final Duration _animationDuration;
Duration? _currentDuration;
double? _previousScrollDelta;
late Rect _dragTargetRelatedToScrollOrigin;
@ -212,10 +217,11 @@ class EdgeDraggingAutoScroller {
///
/// If the scrollable is already scrolling, calling this method updates the
/// previous dragTarget to the new value and continues scrolling if necessary.
void startAutoScrollIfNecessary(Rect dragTarget) {
void startAutoScrollIfNecessary(Rect dragTarget, {Duration? duration}) {
final Offset deltaToOrigin = scrollable.deltaToScrollOrigin;
_dragTargetRelatedToScrollOrigin =
dragTarget.translate(deltaToOrigin.dx, deltaToOrigin.dy);
_currentDuration = duration;
if (_scrolling) {
// The change will be picked up in the next scroll.
return;
@ -228,9 +234,10 @@ class EdgeDraggingAutoScroller {
void stopAutoScroll() {
_scrolling = false;
_previousScrollDelta = null;
_currentDuration = null;
}
Future<void> _scroll({Duration? duration}) async {
Future<void> _scroll() async {
try {
final RenderBox scrollRenderBox =
scrollable.context.findRenderObject()! as RenderBox;
@ -354,13 +361,13 @@ class EdgeDraggingAutoScroller {
}
await scrollable.position.moveTo(
newOffset,
duration: duration,
duration: _currentDuration ?? _animationDuration,
curve: Curves.linear,
clamp: true,
// clamp: true,
);
onScrollViewScrolled?.call();
if (_scrolling) {
await _scroll(duration: duration);
await _scroll();
}
} catch (e) {
debugPrint(e.toString());