Skip to content

Commit

Permalink
add infinity scroll in comic mode
Browse files Browse the repository at this point in the history
  • Loading branch information
appdevelpo committed Feb 7, 2024
1 parent 9215622 commit defb36b
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 145 deletions.
111 changes: 100 additions & 11 deletions lib/controllers/watch/comic_controller.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'dart:io';

import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Expand Down Expand Up @@ -35,17 +34,19 @@ class ComicController extends ReaderController<ExtensionMangaWatch> {
};
final String setting = MiruStorage.getSetting(SettingKey.readingMode);
// final readType = MangaReadMode.standard.obs;

final currentScale = 1.0.obs;
// 当前页码
final currentPage = 0.obs;
final pageController = ExtendedPageController().obs;
final itemPositionsListener = ItemPositionsListener.create();
final alignMode = Alignment.bottomLeft.obs;
// 是否已经恢复上次阅读
final isRecover = false.obs;
final batteryLevel = 100.obs;
final readType = MangaReadMode.standard.obs;
final globalScrollController = ScrollController();
Timer? _barreryTimer;
final currentOffset = 0.0.obs;
final statusBarElement = <String, RxBool>{
'reader-settings.battery'.i18n: true.obs,
'reader-settings.time'.i18n: true.obs,
Expand All @@ -68,6 +69,14 @@ class ComicController extends ReaderController<ExtensionMangaWatch> {
@override
void onInit() async {
_initSetting();
getContent();
// getTartgetContent(playIndex);
Timer.periodic(const Duration(milliseconds: 500), (timer) {
if (globalItemScrollController.isAttached) {
globalItemScrollController.jumpTo(index: index.value);
timer.cancel();
}
});
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
enableWakeLock.value = MiruStorage.getSetting(SettingKey.enableWakelock);
WakelockPlus.toggle(enable: enableWakeLock.value);
Expand All @@ -79,7 +88,7 @@ class ComicController extends ReaderController<ExtensionMangaWatch> {
return;
}
final pos = itemPositionsListener.itemPositions.value.first;
currentPage.value = pos.index;
currentGlobalProgress.value = pos.index;
});
scrollOffsetListener.changes.listen((event) {
hideControlPanel();
Expand All @@ -88,32 +97,46 @@ class ComicController extends ReaderController<ExtensionMangaWatch> {
super.height.value = callback;
});
ever(readType, (callback) {
_jumpPage(currentPage.value);
_jumpPage(currentGlobalProgress.value);
// 保存设置
DatabaseService.setMangaReaderType(
super.detailUrl,
callback,
);
});
// 如果切换章节,重置当前页码
ever(super.index, (callback) => currentPage.value = 0);
// ever(super.index, (callback) => currentPage.value = 0);
//control footer 的 slider 改變時,更新頁碼
ever(progress, (callback) {
// 防止逆向回饋
if (!updateSlider.value) {
return;
}
currentPage.value = callback;
currentGlobalProgress.value = callback;
_jumpPage(callback);
});
ever(currentPage, (callback) {
progress.value = callback;
ever(currentGlobalProgress, (callback) {
if (updateSlider.value) {
progress.value = callback;
}
updateSlider.value = false;
int fullIndex = 0;
debugPrint(currentLocalProgress.value.toString());
for (int i = 0; i < itemlength.length; i++) {
fullIndex += itemlength[i];
if (fullIndex > callback) {
index.value = i;
super.index.value = i;
currentLocalProgress.value = callback - (fullIndex - itemlength[i]);
break;
}
}
});
ever(super.watchData, (callback) async {
if (isRecover.value || callback == null) {
return;
}
getTartgetContent(playIndex);
isRecover.value = true;
// 获取上次阅读的页码
final history = await DatabaseService.getHistoryByPackageAndUrl(
Expand All @@ -127,12 +150,32 @@ class ComicController extends ReaderController<ExtensionMangaWatch> {
history.episodeId != index.value) {
return;
}
currentPage.value = int.parse(history.progress);
_jumpPage(currentPage.value);
currentGlobalProgress.value = int.parse(history.progress);
_jumpPage(currentGlobalProgress.value);
// jumpScroller(index.value);
});
super.onInit();
}

getTartgetContent(int targetIndex) async {
try {
if (targetIndex < 0 || targetIndex == itemlength.length) {
return;
}
final dynamic updatedData =
await runtime.watch(playList[targetIndex].url);
// if (targetIndex < index.value && items[targetIndex].isEmpty) {
// _jumpPage(itemlength[targetIndex] + currentGlobalProgress.value);
// }
items[targetIndex] = updatedData.urls as List<String>;
itemlength[targetIndex] = updatedData.urls.length;
isScrollEnd.value = false;
// index.value = targetIndex;
} catch (e) {
error.value = e.toString();
}
}

onKey(RawKeyEvent event) {
// 按下 ctrl
isZoom.value = event.isControlPressed;
Expand Down Expand Up @@ -171,6 +214,17 @@ class ComicController extends ReaderController<ExtensionMangaWatch> {
);
}

jumpScroller(int pos) async {
if (readType.value == MangaReadMode.webTonn) {
if (globalItemScrollController.isAttached) {
globalItemScrollController.jumpTo(
index: pos,
);
}
return;
}
}

_jumpPage(int page) async {
if (readType.value == MangaReadMode.webTonn) {
if (itemScrollController.isAttached) {
Expand Down Expand Up @@ -221,13 +275,32 @@ class ComicController extends ReaderController<ExtensionMangaWatch> {
}
}

@override
Future<void> loadNextChapter() async {
await getTartgetContent(index.value + 1);
return;
}

Future<void> loadPrevChapter() async {
await getTartgetContent(index.value - 1);
if (itemScrollController.isAttached) {
itemScrollController.scrollTo(
index: itemlength[index.value - 1],
duration: const Duration(milliseconds: 10));
return;
}
if (pageController.value.hasClients) {
pageController.value.jumpToPage(itemlength[index.value - 1]);
}
}

@override
void onClose() async {
if (super.watchData.value != null) {
// 获取所有页数量
final pages = super.watchData.value!.urls.length;
super.addHistory(
currentPage.value.toString(),
currentGlobalProgress.value.toString(),
pages.toString(),
);
}
Expand All @@ -251,4 +324,20 @@ class ComicController extends ReaderController<ExtensionMangaWatch> {
}
super.onClose();
}

@override
void nextChap() {
watchData.value = null;
clearData();
index.value++;
getContent();
}

@override
void prevChap() {
watchData.value = null;
clearData();
index.value--;
getContent();
}
}
9 changes: 9 additions & 0 deletions lib/controllers/watch/novel_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,13 @@ class NovelController extends ReaderController<ExtensionFikushonWatch> {
mouseTimer?.cancel();
super.onClose();
}

@override
void nextPage() {}
@override
void previousPage() {}
@override
void nextChap() {}
@override
void prevChap() {}
}
48 changes: 40 additions & 8 deletions lib/controllers/watch/reader_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'package:miru_app/models/index.dart';
import 'package:miru_app/utils/miru_storage.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';

class ReaderController<T> extends GetxController {
abstract class ReaderController<T> extends GetxController {
final String title;
final List<ExtensionEpisode> playList;
final String detailUrl;
Expand All @@ -33,13 +33,17 @@ class ReaderController<T> extends GetxController {

late Rx<T?> watchData = Rx(null);
final error = ''.obs;
final globalItemPositionsListener = ItemPositionsListener.create();
final globalItemScrollController = ItemScrollController();
final isShowControlPanel = false.obs;
late final index = playIndex.obs;
late final progress = 0.obs;
get cuurentPlayUrl => playList[index.value].url;
Timer? autoScrollTimer;
final isScrolled = true.obs;
final updateSlider = true.obs;
final isInfinityScrollMode = false.obs;
final isLoading = false.obs;
//點擊區域是否反轉
final RxBool tapRegionIsReversed = false.obs;
final dynamic _nextPageHitBox =
Expand All @@ -61,15 +65,22 @@ class ReaderController<T> extends GetxController {
Timer? mouseTimer;
final RxBool enableWakeLock = false.obs;
final RxBool enableFullScreen = false.obs;
// final readType = MangaReadMode.standard.obs;
late final RxList<List<String>> items =
List.filled(playList.length, <String>[]).obs;
late final List<int> itemlength = List.filled(playList.length, 0);
final currentGlobalProgress = 0.obs;
final currentLocalProgress = 0.obs;
@override
void onInit() {
getContent();
// getContent();
autoScrollInterval.value = _autoScrollInterval;
autoScrollOffset.value = _autoScrollOffset;
nextPageHitBox.value = _nextPageHitBox;
prevPageHitBox.value = _prevPageHitBox;
ever(index, (callback) => getContent());
// ever(index, (callback) {
// getContent();
// });

ever(enableAutoScroll, (callback) {
if (callback) {
autoScrollTimer = Timer.periodic(
Expand All @@ -87,7 +98,6 @@ class ReaderController<T> extends GetxController {
autoScrollTimer?.cancel();
});
mouseTimer = Timer.periodic(const Duration(milliseconds: 300), (timer) {
debugPrint(setControllPanel.toString());
if (setControllPanel.value) {
isShowControlPanel.value = true;
return;
Expand All @@ -100,16 +110,38 @@ class ReaderController<T> extends GetxController {
getContent() async {
try {
error.value = '';
watchData.value = null;
// watchData.value = null;
watchData.value = await runtime.watch(cuurentPlayUrl) as T;
itemlength[index.value] = (watchData.value as dynamic)?.urls.length;
items[index.value] = (watchData.value as dynamic)?.urls;
} catch (e) {
error.value = e.toString();
}
}

void previousPage() {}
localToGloabalProgress(int localProgress) {
int progress = 0;
for (int i = 0; i < index.value; i++) {
progress += itemlength[i];
}
progress = localProgress.toInt() + progress;
return progress;
}

void previousPage();

void nextPage();
void loadNextChapter() {}
void nextChap();
void prevChap();

void nextPage() {}
void clearData() {
itemlength.fillRange(0, itemlength.length, 0);
items.fillRange(0, items.length, []);
progress.value = 0;
currentGlobalProgress.value = 0;
currentLocalProgress.value = 0;
}

hideControlPanel() {
setControllPanel.value = false;
Expand Down
Loading

0 comments on commit defb36b

Please sign in to comment.