diff --git a/lib/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart b/lib/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart index fd1788a35..66fbe61d1 100644 --- a/lib/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart +++ b/lib/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart @@ -13,7 +13,7 @@ class ImageHorizontalDragGestureRecognizer Offset? _initialPosition; - final double width; + double width; final TransformationController transformationController; @override diff --git a/lib/common/widgets/interactiveviewer_gallery/interactive_viewer_boundary.dart b/lib/common/widgets/interactiveviewer_gallery/interactive_viewer_boundary.dart index 84675b2b0..f686f4d0c 100644 --- a/lib/common/widgets/interactiveviewer_gallery/interactive_viewer_boundary.dart +++ b/lib/common/widgets/interactiveviewer_gallery/interactive_viewer_boundary.dart @@ -1,5 +1,8 @@ +import 'dart:ui' as ui; + import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactive_viewer.dart' as custom; +import 'package:PiliPlus/common/widgets/only_layout_widget.dart'; import 'package:flutter/material.dart'; /// https://github.com/qq326646683/interactiveviewer_gallery @@ -53,15 +56,16 @@ class InteractiveViewerBoundary extends StatefulWidget { class InteractiveViewerBoundaryState extends State with SingleTickerProviderStateMixin { - late TransformationController _controller; - late AnimationController _animateController; - late Animation _slideAnimation; - late Animation _scaleAnimation; - late Animation _opacityAnimation; + late final TransformationController _controller; + late final AnimationController _animateController; + late final Animation _opacityAnimation; + double dx = 0, dy = 0; Offset _offset = Offset.zero; bool _dragging = false; + late Size _size; + bool get _isActive => _dragging || _animateController.isAnimating; @override @@ -74,35 +78,42 @@ class InteractiveViewerBoundaryState extends State vsync: this, ); - _updateMoveAnimation(); - - _scaleAnimation = _animateController.drive( - Tween(begin: 1.0, end: 0.25), - ); - _opacityAnimation = _animateController.drive( DecorationTween( begin: const BoxDecoration(color: Colors.black), end: const BoxDecoration(color: Colors.transparent), ), ); + + _animateController.addListener(_updateTransformation); } - @override - void dispose() { - _animateController.dispose(); - super.dispose(); + void _updateTransformation() { + final val = _animateController.value; + final scale = ui.lerpDouble(1.0, 0.25, val)!; + + // Matrix4.identity() + // ..translateByDouble(size.width / 2, size.height / 2, 0, 1) + // ..translateByDouble(size.width * val * dx, size.height * val * dy, 0, 1) + // ..scaleByDouble(scale, scale, 1, 1) + // ..translateByDouble(-size.width / 2, -size.height / 2, 0, 1); + + final tmp = (1.0 - scale) / 2.0; + _controller.value = Matrix4.diagonal3Values(scale, scale, scale) + ..setTranslationRaw( + _size.width * (val * dx + tmp), + _size.height * (val * dy + tmp), + 0, + ); } void _updateMoveAnimation() { - final double endX = _offset.dx.sign * (_offset.dx.abs() / _offset.dy.abs()); - final double endY = _offset.dy.sign; - _slideAnimation = _animateController.drive( - Tween( - begin: Offset.zero, - end: Offset(endX, endY), - ), - ); + dy = _offset.dy.sign; + if (dy == 0) { + dx = 0; + } else { + dx = _offset.dx / _offset.dy.abs(); + } } void _handleDragStart(ScaleStartDetails details) { @@ -114,7 +125,7 @@ class InteractiveViewerBoundaryState extends State _offset = Offset.zero; _animateController.value = 0.0; } - setState(_updateMoveAnimation); + _updateMoveAnimation(); } void _handleDragUpdate(ScaleUpdateDetails details) { @@ -123,11 +134,10 @@ class InteractiveViewerBoundaryState extends State } _offset += details.focalPointDelta; - - setState(_updateMoveAnimation); + _updateMoveAnimation(); if (!_animateController.isAnimating) { - _animateController.value = _offset.dy.abs() / context.size!.height; + _animateController.value = _offset.dy.abs() / _size.height; } } @@ -153,29 +163,32 @@ class InteractiveViewerBoundaryState extends State } } - Widget get content => DecoratedBoxTransition( - decoration: _opacityAnimation, - child: SlideTransition( - position: _slideAnimation, - child: ScaleTransition( - scale: _scaleAnimation, - child: widget.child, - ), - ), - ); + @override + void dispose() { + _animateController + ..removeListener(_updateTransformation) + ..dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { - return custom.InteractiveViewer( - maxScale: widget.maxScale, - minScale: widget.minScale, - transformationController: _controller, - onPanStart: _handleDragStart, - onPanUpdate: _handleDragUpdate, - onPanEnd: _handleDragEnd, - onInteractionEnd: widget.onInteractionEnd, - isAnimating: () => _animateController.value != 0, - child: content, + return LayoutSizeWidget( + onPerformLayout: (size) => _size = size, + child: DecoratedBoxTransition( + decoration: _opacityAnimation, + child: custom.InteractiveViewer( + maxScale: widget.maxScale, + minScale: widget.minScale, + transformationController: _controller, + onPanStart: _handleDragStart, + onPanUpdate: _handleDragUpdate, + onPanEnd: _handleDragEnd, + onInteractionEnd: widget.onInteractionEnd, + isAnimating: () => _animateController.value != 0, + child: widget.child, + ), + ), ); } } diff --git a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart index 829100262..67bccec4c 100644 --- a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart +++ b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart @@ -83,10 +83,20 @@ class _InteractiveviewerGalleryState extends State /// The controller to animate the transformation value of the /// [InteractiveViewer] when it should reset. late AnimationController _animationController; - Animation? _animation; + + late final _tween = Matrix4Tween(); + late final _animatable = _tween.chain(CurveTween(curve: Curves.easeOut)); + + late final _horizontalDragGestureRecognizer = + ImageHorizontalDragGestureRecognizer( + width: 0, + transformationController: _transformationController, + ); late Offset _doubleTapLocalPosition; + late double _width; + late final RxInt currentIndex = widget.initIndex.obs; late final int _quality = Pref.previewQ; @@ -113,7 +123,16 @@ class _InteractiveviewerGalleryState extends State } void listener() { - _transformationController.value = _animation?.value ?? Matrix4.identity(); + _transformationController.value = _animatable.evaluate( + _animationController, + ); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _width = MediaQuery.widthOf(context); + _horizontalDragGestureRecognizer.width = _width; } @override @@ -125,6 +144,7 @@ class _InteractiveviewerGalleryState extends State ..removeListener(listener) ..dispose(); _transformationController.dispose(); + _horizontalDragGestureRecognizer.dispose(); if (widget.quality != _quality) { for (final item in widget.sources) { if (item.sourceType == SourceType.networkImage) { @@ -159,13 +179,9 @@ class _InteractiveviewerGalleryState extends State if (_transformationController.value != Matrix4.identity()) { // animate the reset for the transformation of the interactive viewer - _animation = _animationController.drive( - Matrix4Tween( - begin: _transformationController.value, - end: Matrix4.identity(), - ).chain(CurveTween(curve: Curves.easeOut)), - ); - + _tween + ..begin = _transformationController.value.clone() + ..end = Matrix4.identity(); _animationController.forward(from: 0); } } @@ -181,13 +197,12 @@ class _InteractiveviewerGalleryState extends State @override Widget build(BuildContext context) { - final width = MediaQuery.widthOf(context); return Stack( clipBehavior: Clip.none, children: [ InteractiveViewerBoundary( controller: _transformationController, - boundaryWidth: width, + boundaryWidth: _width, maxScale: widget.maxScale, minScale: widget.minScale, onDismissed: Get.back, @@ -231,11 +246,7 @@ class _InteractiveviewerGalleryState extends State : _itemBuilder(index, item), ); }, - horizontalDragGestureRecognizer: - ImageHorizontalDragGestureRecognizer( - width: width, - transformationController: _transformationController, - ), + horizontalDragGestureRecognizer: _horizontalDragGestureRecognizer, ), ), Positioned( @@ -315,8 +326,8 @@ class _InteractiveviewerGalleryState extends State } void onDoubleTap() { - Matrix4 matrix = _transformationController.value.clone(); - double currentScale = matrix.storage[0]; + final matrix = _transformationController.value.clone(); + final currentScale = matrix.storage[0]; double targetScale = widget.minScale; @@ -324,38 +335,26 @@ class _InteractiveviewerGalleryState extends State targetScale = widget.maxScale * 0.4; } - double offSetX = targetScale == 1.0 - ? 0.0 - : -_doubleTapLocalPosition.dx * (targetScale - 1); - double offSetY = targetScale == 1.0 - ? 0.0 - : -_doubleTapLocalPosition.dy * (targetScale - 1); + final double dx, dy; + if (targetScale == 1.0) { + dx = dy = 0; + } else { + final tmp = 1 - targetScale; + dx = _doubleTapLocalPosition.dx * tmp; + dy = _doubleTapLocalPosition.dy * tmp; + } - matrix = Matrix4.fromList([ - targetScale, - matrix.row1.x, - matrix.row2.x, - matrix.row3.x, - matrix.row0.y, - targetScale, - matrix.row2.y, - matrix.row3.y, - matrix.row0.z, - matrix.row1.z, - targetScale, - matrix.row3.z, - offSetX, - offSetY, - matrix.row2.w, - matrix.row3.w, - ]); + matrix + ..[0] = targetScale + ..[5] = targetScale + ..[10] = targetScale + ..[12] = dx + ..[13] = dy; + + _tween + ..begin = _transformationController.value.clone() + ..end = matrix; - _animation = _animationController.drive( - Matrix4Tween( - begin: _transformationController.value, - end: matrix, - ).chain(CurveTween(curve: Curves.easeOut)), - ); _animationController .forward(from: 0) .whenComplete(() => _onScaleChanged(targetScale)); diff --git a/lib/common/widgets/only_layout_widget.dart b/lib/common/widgets/only_layout_widget.dart index 2dcc67074..491b7ba2d 100644 --- a/lib/common/widgets/only_layout_widget.dart +++ b/lib/common/widgets/only_layout_widget.dart @@ -15,17 +15,20 @@ class OnlyLayoutWidget extends SingleChildRenderObjectWidget { @override RenderObject createRenderObject(BuildContext context) => - Layout(onPerformLayout: onPerformLayout); + NoRenderLayoutBox(onPerformLayout: onPerformLayout); @override - void updateRenderObject(BuildContext context, Layout renderObject) { + void updateRenderObject( + BuildContext context, + NoRenderLayoutBox renderObject, + ) { super.updateRenderObject(context, renderObject); renderObject.onPerformLayout = onPerformLayout; } } -class Layout extends RenderProxyBox { - Layout({required this.onPerformLayout}); +class NoRenderLayoutBox extends RenderProxyBox { + NoRenderLayoutBox({required this.onPerformLayout}); LayoutCallback onPerformLayout; @@ -40,3 +43,42 @@ class Layout extends RenderProxyBox { @override void paint(PaintingContext context, Offset offset) {} } + +class LayoutSizeWidget extends SingleChildRenderObjectWidget { + const LayoutSizeWidget({ + super.key, + super.child, + required this.onPerformLayout, + }); + + final LayoutCallback onPerformLayout; + + @override + RenderObject createRenderObject(BuildContext context) => + RenderLayoutBox(onPerformLayout: onPerformLayout); + + @override + void updateRenderObject( + BuildContext context, + RenderLayoutBox renderObject, + ) { + super.updateRenderObject(context, renderObject); + renderObject.onPerformLayout = onPerformLayout; + } +} + +class RenderLayoutBox extends RenderProxyBox { + RenderLayoutBox({required this.onPerformLayout}); + + LayoutCallback onPerformLayout; + + Size? _size; + + @override + void performLayout() { + super.performLayout(); + if (_size != size) { + onPerformLayout(_size = size); + } + } +} diff --git a/lib/plugin/pl_player/widgets/app_bar_ani.dart b/lib/plugin/pl_player/widgets/app_bar_ani.dart index 246fd697c..1699a2ab4 100644 --- a/lib/plugin/pl_player/widgets/app_bar_ani.dart +++ b/lib/plugin/pl_player/widgets/app_bar_ani.dart @@ -48,9 +48,7 @@ class AppBarAni extends StatelessWidget { @override Widget build(BuildContext context) { return SlideTransition( - position: isTop - ? controller.drive(_topPos) - : controller.drive(_bottomPos), + position: controller.drive(isTop ? _topPos : _bottomPos), child: DecoratedBox( decoration: BoxDecoration( gradient: isTop ? _topDecoration : _bottomDecoration, diff --git a/lib/utils/page_utils.dart b/lib/utils/page_utils.dart index 615497c36..cd60c3232 100644 --- a/lib/utils/page_utils.dart +++ b/lib/utils/page_utils.dart @@ -575,24 +575,17 @@ abstract final class PageUtils { List imgList, int index, ) { - final animController = AnimationController( - vsync: state, - duration: Duration.zero, - reverseDuration: Duration.zero, - ); state.showBottomSheet( constraints: const BoxConstraints(), (context) => InteractiveviewerGallery( sources: imgList, initIndex: index, quality: GlobalData().imgQuality, - onClose: animController.dispose, ), enableDrag: false, elevation: 0.0, backgroundColor: Colors.transparent, - transitionAnimationController: animController, - sheetAnimationStyle: const AnimationStyle(duration: Duration.zero), + sheetAnimationStyle: AnimationStyle.noAnimation, ); }