1
1
import 'dart:async' ;
2
+ import 'dart:math' as math;
2
3
import 'package:anymex/utils/logger.dart' ;
3
4
import 'package:anymex/controllers/offline/offline_storage_controller.dart' ;
4
5
import 'package:anymex/controllers/service_handler/params.dart' ;
@@ -88,6 +89,13 @@ class ReaderController extends GetxController with WidgetsBindingObserver {
88
89
RxBool canGoNext = false .obs;
89
90
RxBool canGoPrev = false .obs;
90
91
92
+ final RxBool isOverscrolling = false .obs;
93
+ final RxDouble overscrollProgress = 0.0 .obs;
94
+ final RxBool isOverscrollingNext = true .obs;
95
+ double _overscrollStartOffset = 0.0 ;
96
+ final double _maxOverscrollDistance = 50.0 ;
97
+ Timer ? _overscrollResetTimer;
98
+
91
99
bool _isNavigating = false ;
92
100
93
101
@override
@@ -106,6 +114,7 @@ class ReaderController extends GetxController with WidgetsBindingObserver {
106
114
_performFinalSave ();
107
115
});
108
116
117
+ _overscrollResetTimer? .cancel ();
109
118
pageController? .dispose ();
110
119
super .onClose ();
111
120
}
@@ -217,6 +226,7 @@ class ReaderController extends GetxController with WidgetsBindingObserver {
217
226
scrollOffsetListener = ScrollOffsetListener .create ();
218
227
pageController = PreloadPageController (initialPage: 0 );
219
228
_setupPositionListener ();
229
+ _setupScrollListener ();
220
230
}
221
231
222
232
void _getPreferences () {
@@ -263,16 +273,140 @@ class ReaderController extends GetxController with WidgetsBindingObserver {
263
273
}
264
274
}
265
275
276
+ void _setupScrollListener () {
277
+ if (scrollOffsetListener != null ) {
278
+ scrollOffsetListener! .changes.listen (_onScrollChanged);
279
+ }
280
+ }
281
+
282
+ void _onScrollChanged (double offset) {
283
+ if (! overscrollToChapter.value ||
284
+ readingLayout.value != MangaPageViewMode .continuous ||
285
+ pageList.isEmpty ||
286
+ _isNavigating) {
287
+ return ;
288
+ }
289
+
290
+ final positions = itemPositionsListener? .itemPositions.value;
291
+ if (positions == null || positions.isEmpty) return ;
292
+
293
+ final lastPosition = positions.firstWhere (
294
+ (pos) => pos.index == pageList.length - 1 ,
295
+ orElse: () => positions.first,
296
+ );
297
+
298
+ final isAtLastPage = lastPosition.index == pageList.length - 1 ;
299
+
300
+ final firstPosition = positions.firstWhere (
301
+ (pos) => pos.index == 0 ,
302
+ orElse: () => positions.first,
303
+ );
304
+
305
+ final isAtFirstPage = firstPosition.index == 0 ;
306
+
307
+ // if (isAtLastPage &&
308
+ // canGoNext.value &&
309
+ // lastPosition.itemTrailingEdge <= 1.0) {
310
+ // if (!isOverscrolling.value) {
311
+ // _startOverscroll(true, offset);
312
+ // } else {
313
+ // _updateOverscroll(offset);
314
+ // }
315
+ // } else if (isAtFirstPage &&
316
+ // canGoPrev.value &&
317
+ // firstPosition.itemLeadingEdge >= 0.0) {
318
+ // if (!isOverscrolling.value) {
319
+ // _startOverscroll(false, offset);
320
+ // } else {
321
+ // _updateOverscroll(offset);
322
+ // }
323
+ // } else if (isOverscrolling.value) {
324
+ // _resetOverscroll();
325
+ // }
326
+ }
327
+
328
+ void _startOverscroll (bool isNext, double offset) {
329
+ isOverscrolling.value = true ;
330
+ isOverscrollingNext.value = isNext;
331
+ _overscrollStartOffset = offset;
332
+ overscrollProgress.value = 0.0 ;
333
+
334
+ if (showControls.value) {
335
+ showControls.value = false ;
336
+ }
337
+ }
338
+
339
+ void _updateOverscroll (double currentOffset) {
340
+ final scrollDelta = (currentOffset - _overscrollStartOffset).abs ();
341
+ final progress = (scrollDelta / _maxOverscrollDistance).clamp (0.0 , 1.0 );
342
+
343
+ overscrollProgress.value = progress;
344
+
345
+ if (progress >= 1.0 ) {
346
+ _triggerChapterChange ();
347
+ }
348
+
349
+ _overscrollResetTimer? .cancel ();
350
+ _overscrollResetTimer = Timer (const Duration (milliseconds: 1000 ), () {
351
+ if (overscrollProgress.value < 1.0 ) {
352
+ _resetOverscroll ();
353
+ }
354
+ });
355
+ }
356
+
357
+ void _resetOverscroll () {
358
+ isOverscrolling.value = false ;
359
+ overscrollProgress.value = 0.0 ;
360
+ _overscrollStartOffset = 0.0 ;
361
+ _overscrollResetTimer? .cancel ();
362
+ }
363
+
364
+ void _triggerChapterChange () {
365
+ _resetOverscroll ();
366
+ chapterNavigator (isOverscrollingNext.value);
367
+ }
368
+
266
369
void _onPositionChanged () async {
267
370
if (itemPositionsListener == null || pageList.isEmpty) return ;
268
371
269
372
final positions = itemPositionsListener! .itemPositions.value;
270
373
if (positions.isEmpty || _isNavigating) return ;
271
374
272
- final topItem = currentPageIndex.value >= (pageList.length - 2 )
273
- ? positions.last
274
- : positions.first;
275
- final number = topItem.index + 1 ;
375
+ ItemPosition ? mostVisibleItem;
376
+ double maxVisibleExtent = 0.0 ;
377
+
378
+ final lastItemPosition = positions.firstWhere (
379
+ (pos) => pos.index == pageList.length - 1 ,
380
+ orElse: () => positions.first,
381
+ );
382
+
383
+ final isAtEnd = lastItemPosition.index == pageList.length - 1 &&
384
+ lastItemPosition.itemTrailingEdge <= 1.0 ;
385
+
386
+ for (final position in positions) {
387
+ final leadingEdge = position.itemLeadingEdge;
388
+ final trailingEdge = position.itemTrailingEdge;
389
+
390
+ final visibleExtent =
391
+ (math.min (1.0 , trailingEdge) - math.max (0.0 , leadingEdge))
392
+ .clamp (0.0 , 1.0 );
393
+
394
+ if (isAtEnd && position.index == pageList.length - 1 ) {
395
+ if (visibleExtent > 0.3 ) {
396
+ mostVisibleItem = position;
397
+ break ;
398
+ }
399
+ }
400
+
401
+ if (visibleExtent > maxVisibleExtent) {
402
+ maxVisibleExtent = visibleExtent;
403
+ mostVisibleItem = position;
404
+ }
405
+ }
406
+
407
+ if (mostVisibleItem == null ) return ;
408
+
409
+ final number = mostVisibleItem.index + 1 ;
276
410
277
411
if (! _isValidPageNumber (number)) return ;
278
412
@@ -397,16 +531,6 @@ class ReaderController extends GetxController with WidgetsBindingObserver {
397
531
final current = currentChapter.value;
398
532
if (current == null || current.number == null ) return ;
399
533
400
- // final targetNumber = next ? current.number! + 1 : current.number! - 1;
401
-
402
- // final numberMatchIndex =
403
- // chapterList.indexWhere((chapter) => chapter.number == targetNumber);
404
-
405
- // if (numberMatchIndex != -1) {
406
- // navigateToChapter(numberMatchIndex);
407
- // return;
408
- // }
409
-
410
534
final index = chapterList.indexOf (current);
411
535
if (index == - 1 ) return ;
412
536
@@ -431,6 +555,7 @@ class ReaderController extends GetxController with WidgetsBindingObserver {
431
555
432
556
Future <void > fetchImages (String url) async {
433
557
_isNavigating = true ;
558
+ _resetOverscroll ();
434
559
WidgetsBinding .instance.addPostFrameCallback ((_) => _initTracking ());
435
560
currentPageIndex.value = 1 ;
436
561
_syncAvailability ();
0 commit comments