diff --git a/lib/controllers/watch/comic_controller.dart b/lib/controllers/watch/comic_controller.dart index 7ed5abec..2a2f1c3d 100644 --- a/lib/controllers/watch/comic_controller.dart +++ b/lib/controllers/watch/comic_controller.dart @@ -108,6 +108,7 @@ class ComicController extends ReaderController { } // 下一页 + @override void nextPage() { if (readType.value != MangaReadMode.webTonn) { pageController.value.nextPage( @@ -124,6 +125,7 @@ class ComicController extends ReaderController { } // 上一页 + @override void previousPage() { if (readType.value != MangaReadMode.webTonn) { pageController.value.previousPage( diff --git a/lib/controllers/watch/reader_controller.dart b/lib/controllers/watch/reader_controller.dart index e1832136..02f899dc 100644 --- a/lib/controllers/watch/reader_controller.dart +++ b/lib/controllers/watch/reader_controller.dart @@ -44,14 +44,16 @@ class ReaderController extends GetxController { try { error.value = ''; watchData.value = null; - watchData.value = await runtime.watch( - cuurentPlayUrl, - ) as T; + watchData.value = await runtime.watch(cuurentPlayUrl) as T; } catch (e) { error.value = e.toString(); } } + void previousPage() {} + + void nextPage() {} + showControlPanel() { isShowControlPanel.value = true; _timer?.cancel(); diff --git a/lib/utils/layout.dart b/lib/utils/layout.dart index c02e00aa..3d036df8 100644 --- a/lib/utils/layout.dart +++ b/lib/utils/layout.dart @@ -5,17 +5,17 @@ class LayoutUtils { static bool? _isTablet; // 获取当前宽度 - static double get getWidth { + static double get width { return MediaQuery.of(currentContext).size.width; } // 获取当前高度 - static double get getHeight { + static double get height { return MediaQuery.of(currentContext).size.height; } // 是否是平板 static bool get isTablet { - return _isTablet ??= getWidth > 800; + return _isTablet ??= width > 800; } } diff --git a/lib/views/pages/watch/reader/comic/comic_reader.dart b/lib/views/pages/watch/reader/comic/comic_reader.dart index 8314f7e8..24ac1762 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader.dart @@ -1,14 +1,13 @@ -import 'dart:io'; - import 'package:fluent_ui/fluent_ui.dart'; import 'package:get/get.dart'; import 'package:miru_app/models/extension.dart'; import 'package:miru_app/views/pages/watch/reader/comic/comic_reader_content.dart'; import 'package:miru_app/views/pages/watch/reader/comic/comic_reader_settings.dart'; import 'package:miru_app/controllers/watch/comic_controller.dart'; +import 'package:miru_app/views/widgets/platform_widget.dart'; // import 'package:miru_app/views/pages/watch/reader/comic/comic_zoom.dart'; -import 'package:miru_app/views/widgets/watch/comic_view.dart'; import 'package:miru_app/data/services/extension_service.dart'; +import 'package:miru_app/views/widgets/watch/reader_view.dart'; import 'package:window_manager/window_manager.dart'; class ComicReader extends StatefulWidget { @@ -61,20 +60,13 @@ class _ComicReaderState extends State { @override Widget build(BuildContext context) { - if (Platform.isAndroid) { - return ReaderView( - widget.title, - content: Center( - child: ComicReaderContent(widget.title), - ), - buildSettings: (context) => ComicReaderSettings(widget.title), - ); - } return ReaderView( widget.title, - content: DragToMoveArea( - child: ComicReaderContent(widget.title), - ), + content: PlatformWidget( + androidWidget: ComicReaderContent(widget.title), + desktopWidget: DragToMoveArea( + child: ComicReaderContent(widget.title), + )), buildSettings: (context) => ComicReaderSettings(widget.title), ); } diff --git a/lib/views/pages/watch/reader/comic/comic_reader_content.dart b/lib/views/pages/watch/reader/comic/comic_reader_content.dart index 60fd8db6..fa08729a 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader_content.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader_content.dart @@ -30,6 +30,28 @@ class _ComicReaderContentState extends State { TransformationController transformationController = TransformationController(); final double minScaleValue = 1.0; + + _buildDisplay(Widget child) { + return Stack( + children: [ + child, + Positioned( + bottom: 0, + child: Container( + color: Colors.black.withAlpha(200), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 2), + child: Obx( + () => Text( + "${_c.currentPage.value + 1}/${_c.watchData.value?.urls.length ?? 0}", + style: const TextStyle(color: Colors.white, fontSize: 15), + ), + ), + ), + ), + ], + ); + } + _buildContent() { late Color backgroundColor; if (Platform.isAndroid) { @@ -88,98 +110,65 @@ class _ComicReaderContentState extends State { ); } + // 加载中 if (_c.watchData.value == null) { return const Center(child: ProgressRing()); } - final viewPadding = maxWidth > 800 ? ((maxWidth - 800) / 2) : 0.0; + final viewPadding = maxWidth > 800 ? ((maxWidth - 800) / 2) : 0.0; final images = _c.watchData.value!.urls; final readerType = _c.readType.value; final cuurentPage = _c.currentPage.value; + if (readerType == MangaReadMode.webTonn) { //zooming is inspired by: https://github.com/flutter/flutter/issues/86531 if (Platform.isAndroid) { - return Stack(children: [ - InteractiveViewer( - minScale: minScaleValue, - // maxScale: 2.0, - - transformationController: transformationController, - onInteractionEnd: (ScaleEndDetails endDetails) { + return InteractiveViewer( + minScale: minScaleValue, + transformationController: transformationController, + onInteractionEnd: (ScaleEndDetails endDetails) { + setState(() { + isZoomed = false; + }); + }, + onInteractionUpdate: (x) { + double correctScaleValue = + transformationController.value.getMaxScaleOnAxis(); + if (x.scale == correctScaleValue) { setState(() { isZoomed = false; }); - }, - onInteractionUpdate: (x) { - double correctScaleValue = - transformationController.value.getMaxScaleOnAxis(); - if (correctScaleValue > minScaleValue) {} - if (x.scale == correctScaleValue) { - setState(() { - isZoomed = false; - }); - } - setState(() { - isZoomed = true; - }); - debugPrint("${x.scale}"); - }, - child: ScrollablePositionedList.builder( - padding: EdgeInsets.symmetric( - horizontal: viewPadding, - ), - initialScrollIndex: cuurentPage, - itemScrollController: _c.itemScrollController, - itemPositionsListener: _c.itemPositionsListener, - scrollOffsetController: _c.scrollOffsetController, - scrollOffsetListener: _c.scrolloffsetListener, - physics: isZoomed - ? const NeverScrollableScrollPhysics() - : const ScrollPhysics(), - itemBuilder: (context, index) { - final url = images[index]; - return Obx( - () => CacheNetWorkImagePic( - url, - fit: BoxFit.cover, - headers: _c.watchData.value?.headers, - ), - ); - }, - itemCount: images.length, + } + setState(() { + isZoomed = true; + }); + debugPrint("${x.scale}"); + }, + child: ScrollablePositionedList.builder( + padding: EdgeInsets.symmetric( + horizontal: viewPadding, ), + initialScrollIndex: cuurentPage, + itemScrollController: _c.itemScrollController, + itemPositionsListener: _c.itemPositionsListener, + scrollOffsetController: _c.scrollOffsetController, + scrollOffsetListener: _c.scrolloffsetListener, + physics: isZoomed + ? const NeverScrollableScrollPhysics() + : const ScrollPhysics(), + itemBuilder: (context, index) { + final url = images[index]; + return Obx( + () => CacheNetWorkImagePic( + url, + fit: BoxFit.cover, + headers: _c.watchData.value?.headers, + ), + ); + }, + itemCount: images.length, ), - Positioned.fill( - top: 120, - bottom: 120, - child: GestureDetector( - onTapDown: (TapDownDetails details) { - final xPos = details.globalPosition.dx; - if (xPos > Get.width * 4 / 5) { - return _c.nextPage(); - } - if (xPos < Get.width / 5) { - return _c.previousPage(); - } - }, - )), - Container( - alignment: Alignment.bottomCenter, - margin: const EdgeInsets.only(bottom: 40), - child: Container( - decoration: BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.circular(40), - ), - alignment: Alignment.center, - width: 200, - height: 30, - child: Text( - "${cuurentPage + 1}/${images.length}", - style: const TextStyle( - color: Colors.white, fontSize: 15), - ))) - ]); + ); } return ScrollablePositionedList.builder( padding: EdgeInsets.symmetric( @@ -199,215 +188,32 @@ class _ComicReaderContentState extends State { }, itemCount: images.length, ); - //works but lots of bugs - // return Stack(children: [ - // Obx(() => Transform.scale( - // scale: zoomScale.value, - // child: Transform.translate( - // offset: Offset(x.value, y.value), - // child: ScrollablePositionedList.builder( - // padding: EdgeInsets.symmetric( - // horizontal: viewPadding, - // ), - // initialScrollIndex: cuurentPage, - // itemScrollController: _c.itemScrollController, - // itemPositionsListener: _c.itemPositionsListener, - // scrollOffsetController: _c.scrollOffsetController, - // // physics: const BouncingScrollPhysics(), - // itemBuilder: (context, index) { - // final url = images[index]; - // return Obx( - // () => CacheNetWorkImagePic( - // url, - // fit: BoxFit.cover, - // headers: _c.watchData.value?.headers, - // ), - // ); - // }, - // itemCount: images.length, - // )))), - // Positioned.fill( - // top: 120, - // bottom: 120, - // child: GestureDetector( - // onPanStart: (DragStartDetails detail) { - // initPointer = detail.globalPosition; - // panStart = true; - // debugPrint("start"); - // }, - // onTapDown: (TapDownDetails details) { - // final xPos = details.globalPosition.dx; - // if (xPos > Get.width * 4 / 5) { - // return _c.nextPage(); - // } - // if (xPos < Get.width / 5) { - // return _c.previousPage(); - // } - // }, - // onPanUpdate: (DragUpdateDetails detail) { - // final dx = detail.delta.dx; - // final dy = detail.delta.dy; - // final innerProduct = prevdx * dx + prevdy * dy; - // //zooming event - // if (innerProduct < 0 && innerProduct >= -1.0) { - // Offset pos = detail.globalPosition; - // if (panStart) { - // scaleRange = (pos - initPointer).distance; - // panStart = false; - // prevdist = (pos - initPointer).distanceSquared; - // return; - // } - // if (dx > prevdx) { - // pointer1 = pos; - // } else { - // pointer2 = pos; - // } - // prevPointer = pos; - // // debugPrint("${(pointer1 + pointer2) / 2}"); - // // debugPrint("$maxWidth"); - // final pointerDistance = - // (pointer1 - pointer2).distanceSquared; - // if (pointerDistance < maxWidth * 60) { - // // debugPrint("unzoom"); - // zoomScale.value -= 0.04; - // // return; - // } else { - // // debugPrint("zoom"); - // zoomScale.value += 0.04; - // } - // debugPrint("${zoomScale.value}"); - - // // debugPrint("$pointer1,$pointer2"); - // // debugPrint("$pointerDistance"); - // // debugPrint("${(pointerDistance / scaleRange)}"); - // // final double scale = - // // (pointerDistance - maxWidth) / (scaleRange); - // // debugPrint("$scale"); - // // double s = scale * 2 / 10000 + 1; - // // debugPrint("$s"); - // zoomScale.value = zoomScale.value.clamp(1, 3); - // // debugPrint("$zoomScale"); - // } else { - // x.value += dx; - // // y.value += dy; - // } - - // prevdy = dy; - // prevdx = dx; - // //fixing the conflict between scrolling and gesture widget - // if (dy > 0) { - // _c.scrollWithOffset( - // -innerProduct * zoomScale.value * 3); - // return; - // } - // _c.scrollWithOffset(innerProduct * zoomScale.value * 3); - // }, - // )), - // Container( - // alignment: Alignment.bottomCenter, - // margin: const EdgeInsets.only(bottom: 40), - // child: Container( - // decoration: BoxDecoration( - // color: Colors.blue, - // borderRadius: BorderRadius.circular(40), - // ), - // alignment: Alignment.center, - // width: 200, - // height: 30, - // child: Text( - // "${cuurentPage + 1}/${images.length}", - // style: TextStyle(color: Colors.white, fontSize: 15), - // )), - // ), - // ]); } -//common mode and left to right mode - return Stack(children: [ - ExtendedImageGesturePageView.builder( - itemCount: images.length, - reverse: readerType == MangaReadMode.rightToLeft, - onPageChanged: (index) { - _c.currentPage.value = index; - }, - scrollDirection: Axis.horizontal, - controller: _c.pageController.value, - itemBuilder: (BuildContext context, int index) { - final url = images[index]; - return Container( - padding: EdgeInsets.symmetric( - horizontal: viewPadding, - ), - child: ExtendedImage.network( - url, - mode: ExtendedImageMode.gesture, - key: ValueKey(url), - fit: BoxFit.contain, - headers: _c.watchData.value?.headers, - ), - ); - }), - Positioned.fill( - top: 120, - bottom: 120, - child: GestureDetector( - onTapDown: (TapDownDetails details) { - final xPos = details.globalPosition.dx; - if (xPos > Get.width * 4 / 5) { - if (readerType == MangaReadMode.rightToLeft) { - return _c.previousPage(); - } - return _c.nextPage(); - } - if (xPos < Get.width / 5) { - if (readerType == MangaReadMode.rightToLeft) { - return _c.nextPage(); - } - return _c.previousPage(); - } - }, - )), - Container( - alignment: Alignment.bottomCenter, - margin: const EdgeInsets.only(bottom: 40), - child: Container( - decoration: BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.circular(40), - ), - alignment: Alignment.center, - width: 200, - height: 30, - child: Text( - "${cuurentPage + 1}/${images.length}", - style: const TextStyle(color: Colors.white, fontSize: 15), - )), - ), - ]); - - //old code - - // PageView.builder( - // reverse: readerType == MangaReadMode.rightToLeft, - // controller: _c.pageController.value, - // onPageChanged: (index) { - // _c.currentPage.value = index; - // }, - // itemBuilder: (context, index) { - // final url = images[index]; - // return Container( - // padding: EdgeInsets.symmetric( - // horizontal: viewPadding, - // ), - // child: CacheNetWorkImagePic( - // url, - // key: ValueKey(url), - // fit: BoxFit.contain, - // headers: _c.watchData.value?.headers, - // ), - // ); - // }, - // itemCount: images.length, - // ); + //common mode and left to right mode + return ExtendedImageGesturePageView.builder( + itemCount: images.length, + reverse: readerType == MangaReadMode.rightToLeft, + onPageChanged: (index) { + _c.currentPage.value = index; + }, + scrollDirection: Axis.horizontal, + controller: _c.pageController.value, + itemBuilder: (BuildContext context, int index) { + final url = images[index]; + return Container( + padding: EdgeInsets.symmetric( + horizontal: viewPadding, + ), + child: ExtendedImage.network( + url, + mode: ExtendedImageMode.gesture, + key: ValueKey(url), + fit: BoxFit.contain, + headers: _c.watchData.value?.headers, + ), + ); + }, + ); }); })), ), @@ -418,9 +224,16 @@ class _ComicReaderContentState extends State { Widget build(BuildContext context) { return PlatformBuildWidget( androidBuilder: (context) { - return Scaffold(body: SafeArea(child: _buildContent())); + return Scaffold( + body: SafeArea( + child: _buildDisplay( + _buildContent(), + ), + )); }, - desktopBuilder: (context) => _buildContent(), + desktopBuilder: (context) => _buildDisplay( + _buildContent(), + ), ); } } diff --git a/lib/views/widgets/watch/comic_view.dart b/lib/views/widgets/watch/comic_view.dart deleted file mode 100644 index 5afce70b..00000000 --- a/lib/views/widgets/watch/comic_view.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:miru_app/views/widgets/watch/control_panel_footer.dart'; -import 'package:miru_app/views/widgets/watch/control_panel_header.dart'; -import 'package:miru_app/controllers/watch/reader_controller.dart'; - -class ReaderView extends StatelessWidget { - const ReaderView( - this.tag, { - super.key, - required this.content, - required this.buildSettings, - }); - final String tag; - final Widget content; - final Widget Function(BuildContext context) buildSettings; - - @override - Widget build(BuildContext context) { - final c = Get.find(tag: tag); - return Obx( - () => Stack( - children: [ - MouseRegion( - onHover: (event) { - if (event.position.dy < 60) { - c.showControlPanel(); - } - if (event.position.dy > Get.height - 60) { - c.showControlPanel(); - } - }, - child: content, - ), - - // 点击中间显示控制面板 - if (c.error.value.isEmpty) - Positioned( - top: 120, - bottom: 120, - left: 0, - right: 0, - child: GestureDetector( - onDoubleTap: () { - // 中间点击的话 将不会定时关闭 - c.isShowControlPanel.value = !c.isShowControlPanel.value; - }, - ), - ), - - if (c.isShowControlPanel.value) ...[ - // 顶部控制 - Positioned( - child: ControlPanelHeader( - tag, - buildSettings: buildSettings, - ), - ), - // 底部控制 - Positioned( - right: 0, - left: 0, - bottom: 0, - child: ControlPanelFooter(tag), - ), - ], - ], - ), - ); - } -} diff --git a/lib/views/widgets/watch/control_panel_footer.dart b/lib/views/widgets/watch/control_panel_footer.dart index c80a83a4..de165cd6 100644 --- a/lib/views/widgets/watch/control_panel_footer.dart +++ b/lib/views/widgets/watch/control_panel_footer.dart @@ -19,7 +19,7 @@ class ControlPanelFooter extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: Platform.isAndroid - ? Theme.of(context).colorScheme.background.withOpacity(0.5) + ? Theme.of(context).colorScheme.background.withOpacity(0.9) : Colors.transparent, borderRadius: const BorderRadius.only( topLeft: Radius.circular(40), diff --git a/lib/views/widgets/watch/reader_view.dart b/lib/views/widgets/watch/reader_view.dart index b75b36b0..5bc02245 100644 --- a/lib/views/widgets/watch/reader_view.dart +++ b/lib/views/widgets/watch/reader_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:miru_app/utils/layout.dart'; import 'package:miru_app/views/widgets/watch/control_panel_footer.dart'; import 'package:miru_app/views/widgets/watch/control_panel_header.dart'; import 'package:miru_app/controllers/watch/reader_controller.dart'; @@ -26,7 +27,7 @@ class ReaderView extends StatelessWidget { if (event.position.dy < 60) { c.showControlPanel(); } - if (event.position.dy > Get.height - 60) { + if (event.position.dy > LayoutUtils.height - 60) { c.showControlPanel(); } }, @@ -34,6 +35,7 @@ class ReaderView extends StatelessWidget { ), // 点击中间显示控制面板 + // 左边上一页右边下一页 if (c.error.value.isEmpty) Positioned( top: 120, @@ -41,8 +43,16 @@ class ReaderView extends StatelessWidget { left: 0, right: 0, child: GestureDetector( - onTap: () { - // 中间点击的话 将不会定时关闭 + onTapDown: (TapDownDetails details) { + final xPos = details.globalPosition.dx; + final width = LayoutUtils.width; + final unitWidth = width / 3; + if (xPos < unitWidth) { + return c.previousPage(); + } + if (xPos > unitWidth * 2) { + return c.nextPage(); + } c.isShowControlPanel.value = !c.isShowControlPanel.value; }, ),