diff --git a/lib/common/widgets/colored_box_transition.dart b/lib/common/widgets/colored_box_transition.dart new file mode 100644 index 000000000..9d3df5549 --- /dev/null +++ b/lib/common/widgets/colored_box_transition.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class ColoredBoxTransition extends AnimatedWidget { + const ColoredBoxTransition({ + super.key, + required this.color, + this.child, + }) : super(listenable: color); + + final Animation color; + + final Widget? child; + + @override + Widget build(BuildContext context) { + return ColoredBox( + color: color.value!, + child: child, + ); + } +} diff --git a/lib/common/widgets/image_viewer/gallery_viewer.dart b/lib/common/widgets/image_viewer/gallery_viewer.dart index 3b73cad60..80526be08 100644 --- a/lib/common/widgets/image_viewer/gallery_viewer.dart +++ b/lib/common/widgets/image_viewer/gallery_viewer.dart @@ -17,6 +17,7 @@ import 'dart:io' show File, Platform; +import 'package:PiliPlus/common/widgets/colored_box_transition.dart'; import 'package:PiliPlus/common/widgets/flutter/page/page_view.dart'; import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart'; import 'package:PiliPlus/common/widgets/gesture/image_tap_gesture_recognizer.dart'; @@ -52,7 +53,7 @@ class GalleryViewer extends StatefulWidget { this.maxScale = 8.0, required this.quality, required this.sources, - this.initIndex = 1, + this.initIndex = 0, }); final double minScale; @@ -87,14 +88,12 @@ class _GalleryViewerState extends State late final LongPressGestureRecognizer _longPressGestureRecognizer; late final AnimationController _animateController; - late final Animation _opacityAnimation; + late final Animation _opacityAnimation; double dx = 0, dy = 0; Offset _offset = Offset.zero; bool _dragging = false; - bool get _isActive => _dragging || _animateController.isAnimating; - String _getActualUrl(String url) { return _quality != 100 ? ImageUtils.thumbnailUrl(url, _quality) @@ -138,14 +137,16 @@ class _GalleryViewerState extends State }); _animateController = AnimationController( - duration: const Duration(milliseconds: 300), + duration: const Duration( + milliseconds: 750, + ), // reverse only if value <= 0.2 vsync: this, ); _opacityAnimation = _animateController.drive( - DecorationTween( - begin: const BoxDecoration(color: Colors.black), - end: const BoxDecoration(color: Colors.transparent), + ColorTween( + begin: Colors.black, + end: Colors.transparent, ), ); } @@ -189,7 +190,7 @@ class _GalleryViewerState extends State } void _onDragUpdate(ScaleUpdateDetails details) { - if (!_isActive || _animateController.isAnimating) { + if (!_dragging || _animateController.isAnimating) { return; } @@ -202,16 +203,12 @@ class _GalleryViewerState extends State } void _onDragEnd(ScaleEndDetails details) { - if (!_isActive || _animateController.isAnimating) { + if (!_dragging || _animateController.isAnimating) { return; } _dragging = false; - if (_animateController.isCompleted) { - return; - } - if (!_animateController.isDismissed) { if (_animateController.value > 0.2) { Get.back(); @@ -256,37 +253,35 @@ class _GalleryViewerState extends State return Listener( behavior: .opaque, onPointerDown: _onPointerDown, - child: DecoratedBoxTransition( - decoration: _opacityAnimation, - child: Stack( - fit: .expand, - alignment: .center, - clipBehavior: .none, - children: [ - LayoutBuilder( - builder: (context, constraints) { - _containerSize = constraints.biggest; - return MatrixTransition( - alignment: .topLeft, - animation: _animateController, - onTransform: _onTransform, - child: PageView.builder( - controller: _pageController, - onPageChanged: _onPageChanged, - physics: const CustomTabBarViewScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), - itemCount: widget.sources.length, - itemBuilder: _itemBuilder, - horizontalDragGestureRecognizer: () => - _horizontalDragGestureRecognizer, + child: Stack( + fit: .expand, + alignment: .center, + clipBehavior: .none, + children: [ + ColoredBoxTransition(color: _opacityAnimation), + LayoutBuilder( + builder: (context, constraints) { + _containerSize = constraints.biggest; + return MatrixTransition( + alignment: .topLeft, + animation: _animateController, + onTransform: _onTransform, + child: PageView.builder( + controller: _pageController, + onPageChanged: _onPageChanged, + physics: const CustomTabBarViewScrollPhysics( + parent: AlwaysScrollableScrollPhysics(), ), - ); - }, - ), - _buildIndicator, - ], - ), + itemCount: widget.sources.length, + itemBuilder: _itemBuilder, + horizontalDragGestureRecognizer: () => + _horizontalDragGestureRecognizer, + ), + ); + }, + ), + _buildIndicator, + ], ), ); } diff --git a/lib/common/widgets/image_viewer/viewer.dart b/lib/common/widgets/image_viewer/viewer.dart index c52624a86..1b5de44eb 100644 --- a/lib/common/widgets/image_viewer/viewer.dart +++ b/lib/common/widgets/image_viewer/viewer.dart @@ -77,10 +77,26 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { _GestureType? _gestureType; + final Matrix4 _matrix = Matrix4.identity(); + + late double __scale; + double get _scale => __scale; + set _scale(double value) { + __scale = value; + _matrix[0] = _matrix[5] = _matrix[10] = value; + } + + late Offset __position; + Offset get _position => __position; + set _position(Offset value) { + __position = value; + _matrix + ..[12] = value.dx + ..[13] = value.dy; + } + Offset? _scalePos; - late double _scale; double? _scaleStart; - late Offset _position; Offset? _referenceFocalPoint; late Size _imageSize; @@ -97,10 +113,6 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { late double _scaleFrom, _scaleTo; late Offset _positionFrom, _positionTo; - Matrix4 get _matrix => - Matrix4.translationValues(_position.dx, _position.dy, 0.0) - ..scaleByDouble(_scale, _scale, _scale, 1.0); - void _listener() { final t = Curves.easeOut.transform(_animationController.value); _scale = t.lerp(_scaleFrom, _scaleTo); @@ -396,7 +408,7 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { onPointerDown: _onPointerDown, onPointerPanZoomStart: _onPointerPanZoomStart, onPointerSignal: _onPointerSignal, - child: ClipRRect( + child: ClipRect( child: Transform( transform: _matrix, child: widget.child, diff --git a/lib/pages/setting/models/extra_settings.dart b/lib/pages/setting/models/extra_settings.dart index 78f0dfa66..396541da0 100644 --- a/lib/pages/setting/models/extra_settings.dart +++ b/lib/pages/setting/models/extra_settings.dart @@ -24,7 +24,7 @@ import 'package:PiliPlus/pages/home/controller.dart'; import 'package:PiliPlus/pages/main/controller.dart'; import 'package:PiliPlus/pages/setting/models/model.dart'; import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; -import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart'; +import 'package:PiliPlus/pages/setting/widgets/slider_dialog.dart'; import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/services/download/download_service.dart'; @@ -952,7 +952,7 @@ Future _showRefreshDragDialog( ) async { final res = await showDialog( context: context, - builder: (context) => SlideDialog( + builder: (context) => SliderDialog( title: '刷新滑动距离', min: 0.1, max: 0.5, @@ -975,7 +975,7 @@ Future _showRefreshDialog( ) async { final res = await showDialog( context: context, - builder: (context) => SlideDialog( + builder: (context) => SliderDialog( title: '刷新指示器高度', min: 10.0, max: 100.0, @@ -1063,7 +1063,7 @@ Future _showReplyCountDialog( ) async { final res = await showDialog( context: context, - builder: (context) => SlideDialog( + builder: (context) => SliderDialog( title: '连接重试次数', min: 0, max: 8, @@ -1085,7 +1085,7 @@ Future _showReplyDelayDialog( ) async { final res = await showDialog( context: context, - builder: (context) => SlideDialog( + builder: (context) => SliderDialog( title: '连接重试间隔', min: 0, max: 1000, diff --git a/lib/pages/setting/models/style_settings.dart b/lib/pages/setting/models/style_settings.dart index 0afb20f48..69089ad20 100644 --- a/lib/pages/setting/models/style_settings.dart +++ b/lib/pages/setting/models/style_settings.dart @@ -19,10 +19,10 @@ import 'package:PiliPlus/pages/main/controller.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; import 'package:PiliPlus/pages/setting/models/model.dart'; import 'package:PiliPlus/pages/setting/slide_color_picker.dart'; -import 'package:PiliPlus/pages/setting/widgets/dual_slide_dialog.dart'; +import 'package:PiliPlus/pages/setting/widgets/dual_slider_dialog.dart'; import 'package:PiliPlus/pages/setting/widgets/multi_select_dialog.dart'; import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; -import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart'; +import 'package:PiliPlus/pages/setting/widgets/slider_dialog.dart'; import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/theme_ext.dart'; @@ -373,7 +373,7 @@ void _showQualityDialog({ }) { showDialog( context: context, - builder: (context) => SlideDialog( + builder: (context) => SliderDialog( value: initValue.toDouble(), title: title, min: 10, @@ -622,7 +622,7 @@ void _showSpringDialog(BuildContext context, _) { Future _showFontWeightDialog(BuildContext context) async { final res = await showDialog( context: context, - builder: (context) => SlideDialog( + builder: (context) => SliderDialog( title: 'App字体字重', value: Pref.appFontWeight.toDouble() + 1, min: 1, @@ -661,7 +661,7 @@ Future _showCardWidthDialog( ) async { final res = await showDialog<(double, double)>( context: context, - builder: (context) => DualSlideDialog( + builder: (context) => DualSliderDialog( title: '列表最大列宽度(默认240dp)', value1: Pref.recommendCardWidth, value2: Pref.smallCardWidth, @@ -879,7 +879,7 @@ Future _showToastDialog( ) async { final res = await showDialog( context: context, - builder: (context) => SlideDialog( + builder: (context) => SliderDialog( title: 'Toast不透明度', value: CustomToast.toastOpacity, min: 0.0, diff --git a/lib/pages/setting/widgets/dual_slide_dialog.dart b/lib/pages/setting/widgets/dual_slider_dialog.dart similarity index 92% rename from lib/pages/setting/widgets/dual_slide_dialog.dart rename to lib/pages/setting/widgets/dual_slider_dialog.dart index 7543898e5..95beac98d 100644 --- a/lib/pages/setting/widgets/dual_slide_dialog.dart +++ b/lib/pages/setting/widgets/dual_slider_dialog.dart @@ -1,7 +1,7 @@ import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:flutter/material.dart'; -class DualSlideDialog extends StatefulWidget { +class DualSliderDialog extends StatefulWidget { final double value1; final double value2; final String title; @@ -13,7 +13,7 @@ class DualSlideDialog extends StatefulWidget { final String suffix; final int precise; - const DualSlideDialog({ + const DualSliderDialog({ super.key, required this.value1, required this.value2, @@ -28,10 +28,10 @@ class DualSlideDialog extends StatefulWidget { }); @override - State createState() => _DualSlideDialogState(); + State createState() => _DualSliderDialogState(); } -class _DualSlideDialogState extends State { +class _DualSliderDialogState extends State { late double _tempValue1; late double _tempValue2; diff --git a/lib/pages/setting/widgets/slide_dialog.dart b/lib/pages/setting/widgets/slider_dialog.dart similarity index 89% rename from lib/pages/setting/widgets/slide_dialog.dart rename to lib/pages/setting/widgets/slider_dialog.dart index 38e04e134..b1e18f749 100644 --- a/lib/pages/setting/widgets/slide_dialog.dart +++ b/lib/pages/setting/widgets/slider_dialog.dart @@ -1,7 +1,7 @@ import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:flutter/material.dart'; -class SlideDialog extends StatefulWidget { +class SliderDialog extends StatefulWidget { final double value; final String title; final double min; @@ -10,7 +10,7 @@ class SlideDialog extends StatefulWidget { final String suffix; final int precise; - const SlideDialog({ + const SliderDialog({ super.key, required this.value, required this.title, @@ -22,10 +22,10 @@ class SlideDialog extends StatefulWidget { }); @override - State createState() => _SlideDialogState(); + State createState() => _SliderDialogState(); } -class _SlideDialogState extends State { +class _SliderDialogState extends State { late double _tempValue; @override diff --git a/lib/pages/video/reply_reply/view.dart b/lib/pages/video/reply_reply/view.dart index c16053636..100a6de97 100644 --- a/lib/pages/video/reply_reply/view.dart +++ b/lib/pages/video/reply_reply/view.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/common/skeleton/video_reply.dart'; +import 'package:PiliPlus/common/widgets/colored_box_transition.dart'; import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart'; import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; @@ -327,8 +328,8 @@ class _VideoReplyReplyPanelState extends State } final child = _replyItem(context, response[index], index); if (jumpIndex == index) { - return AnimatedBuilder( - animation: _colorAnimation ??= _controller.animController.drive( + return ColoredBoxTransition( + color: _colorAnimation ??= _controller.animController.drive( ColorTween( begin: theme.colorScheme.onInverseSurface, end: theme.colorScheme.surface, @@ -339,12 +340,6 @@ class _VideoReplyReplyPanelState extends State ), ), child: child, - builder: (context, child) { - return ColoredBox( - color: _colorAnimation!.value!, - child: child, - ); - }, ); } return child;