#539 viewer: stiffer spring physics for vertical pager

This commit is contained in:
Thibault Deckers 2023-04-29 15:31:35 +02:00
parent 50b4006352
commit 3306bc6340
6 changed files with 51 additions and 21 deletions

View file

@ -84,7 +84,7 @@ class DurationsData {
final Duration tvImageFocusAnimation; final Duration tvImageFocusAnimation;
// viewer animations // viewer animations
final Duration viewerVerticalPageScrollAnimation; final Duration viewerHorizontalPageScrollAnimation;
final Duration viewerOverlayAnimation; final Duration viewerOverlayAnimation;
final Duration viewerOverlayChangeAnimation; final Duration viewerOverlayChangeAnimation;
@ -102,7 +102,7 @@ class DurationsData {
this.staggeredAnimationPageTarget = const Duration(milliseconds: 800), this.staggeredAnimationPageTarget = const Duration(milliseconds: 800),
this.quickChooserAnimation = const Duration(milliseconds: 100), this.quickChooserAnimation = const Duration(milliseconds: 100),
this.tvImageFocusAnimation = const Duration(milliseconds: 150), this.tvImageFocusAnimation = const Duration(milliseconds: 150),
this.viewerVerticalPageScrollAnimation = const Duration(milliseconds: 500), this.viewerHorizontalPageScrollAnimation = const Duration(milliseconds: 400),
this.viewerOverlayAnimation = const Duration(milliseconds: 200), this.viewerOverlayAnimation = const Duration(milliseconds: 200),
this.viewerOverlayChangeAnimation = const Duration(milliseconds: 150), this.viewerOverlayChangeAnimation = const Duration(milliseconds: 150),
}) : staggeredAnimationDelay = staggeredAnimation ~/ 6; }) : staggeredAnimationDelay = staggeredAnimation ~/ 6;
@ -120,7 +120,7 @@ class DurationsData {
staggeredAnimationPageTarget: Duration.zero, staggeredAnimationPageTarget: Duration.zero,
quickChooserAnimation: Duration.zero, quickChooserAnimation: Duration.zero,
tvImageFocusAnimation: Duration.zero, tvImageFocusAnimation: Duration.zero,
viewerVerticalPageScrollAnimation: Duration.zero, viewerHorizontalPageScrollAnimation: Duration.zero,
viewerOverlayAnimation: Duration.zero, viewerOverlayAnimation: Duration.zero,
viewerOverlayChangeAnimation: Duration.zero, viewerOverlayChangeAnimation: Duration.zero,
); );

View file

@ -0,0 +1,20 @@
import 'package:flutter/widgets.dart';
class SpringyScrollPhysics extends ScrollPhysics {
@override
final SpringDescription spring;
const SpringyScrollPhysics({
required this.spring,
super.parent,
});
@override
SpringyScrollPhysics applyTo(ScrollPhysics? ancestor) {
return SpringyScrollPhysics(
spring: spring,
parent: buildParent(ancestor),
);
}
}

View file

@ -10,6 +10,7 @@ import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/common/behaviour/springy_scroll_physics.dart';
import 'package:aves/widgets/viewer/action/entry_action_delegate.dart'; import 'package:aves/widgets/viewer/action/entry_action_delegate.dart';
import 'package:aves/widgets/viewer/controls/controller.dart'; import 'package:aves/widgets/viewer/controls/controller.dart';
import 'package:aves/widgets/viewer/controls/intents.dart'; import 'package:aves/widgets/viewer/controls/intents.dart';
@ -37,6 +38,13 @@ class ViewerVerticalPageView extends StatefulWidget {
final VoidCallback onImagePageRequested; final VoidCallback onImagePageRequested;
final void Function(AvesEntry mainEntry, AvesEntry? pageEntry) onViewDisposed; final void Function(AvesEntry mainEntry, AvesEntry? pageEntry) onViewDisposed;
// critically damped spring a bit stiffer than `ScrollPhysics._kDefaultSpring`
static final spring = SpringDescription.withDampingRatio(
mass: 0.5,
stiffness: 200.0,
ratio: 1.0,
);
const ViewerVerticalPageView({ const ViewerVerticalPageView({
super.key, super.key,
required this.collection, required this.collection,
@ -180,7 +188,9 @@ class _ViewerVerticalPageViewState extends State<ViewerVerticalPageView> {
controller: widget.verticalPager, controller: widget.verticalPager,
physics: MagnifierScrollerPhysics( physics: MagnifierScrollerPhysics(
gestureSettings: context.select<MediaQueryData, DeviceGestureSettings>((mq) => mq.gestureSettings), gestureSettings: context.select<MediaQueryData, DeviceGestureSettings>((mq) => mq.gestureSettings),
parent: const PageScrollPhysics(), parent: SpringyScrollPhysics(
spring: ViewerVerticalPageView.spring,
),
), ),
onPageChanged: widget.onVerticalPageChanged, onPageChanged: widget.onVerticalPageChanged,
children: pages, children: pages,

View file

@ -9,6 +9,7 @@ import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/filters/trash.dart';
import 'package:aves/model/highlight.dart'; import 'package:aves/model/highlight.dart';
import 'package:aves/model/settings/enums/accessibility_animations.dart';
import 'package:aves/model/settings/enums/accessibility_timeout.dart'; import 'package:aves/model/settings/enums/accessibility_timeout.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
@ -62,7 +63,7 @@ class EntryViewerStack extends StatefulWidget {
State<EntryViewerStack> createState() => _EntryViewerStackState(); State<EntryViewerStack> createState() => _EntryViewerStackState();
} }
class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewControllerMixin, FeedbackMixin, SingleTickerProviderStateMixin { class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewControllerMixin, FeedbackMixin, TickerProviderStateMixin {
final Floating _floating = Floating(); final Floating _floating = Floating();
late int _currentEntryIndex; late int _currentEntryIndex;
late ValueNotifier<int> _currentVerticalPage; late ValueNotifier<int> _currentVerticalPage;
@ -72,7 +73,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
final ValueNotifier<bool> _overlayVisible = ValueNotifier(true); final ValueNotifier<bool> _overlayVisible = ValueNotifier(true);
final ValueNotifier<bool> _viewLocked = ValueNotifier(false); final ValueNotifier<bool> _viewLocked = ValueNotifier(false);
final ValueNotifier<bool> _overlayExpandedNotifier = ValueNotifier(false); final ValueNotifier<bool> _overlayExpandedNotifier = ValueNotifier(false);
late AnimationController _overlayAnimationController; late AnimationController _verticalPageAnimationController, _overlayAnimationController;
late Animation<double> _overlayButtonScale, _overlayVideoControlScale, _overlayOpacity; late Animation<double> _overlayButtonScale, _overlayVideoControlScale, _overlayOpacity;
late Animation<Offset> _overlayTopOffset; late Animation<Offset> _overlayTopOffset;
EdgeInsets? _frozenViewInsets, _frozenViewPadding; EdgeInsets? _frozenViewInsets, _frozenViewPadding;
@ -125,6 +126,8 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
_currentVerticalPage = ValueNotifier(imagePage); _currentVerticalPage = ValueNotifier(imagePage);
_horizontalPager = PageController(initialPage: _currentEntryIndex); _horizontalPager = PageController(initialPage: _currentEntryIndex);
_verticalPager = PageController(initialPage: _currentVerticalPage.value)..addListener(_onVerticalPageControllerChanged); _verticalPager = PageController(initialPage: _currentVerticalPage.value)..addListener(_onVerticalPageControllerChanged);
_verticalPageAnimationController = AnimationController.unbounded(vsync: this);
_verticalPageAnimationController.addListener(() => _verticalPager.jumpTo(_verticalPageAnimationController.value));
_overlayAnimationController = AnimationController( _overlayAnimationController = AnimationController(
duration: context.read<DurationsData>().viewerOverlayAnimation, duration: context.read<DurationsData>().viewerOverlayAnimation,
vsync: this, vsync: this,
@ -171,6 +174,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
_floating.dispose(); _floating.dispose();
cleanEntryControllers(entryNotifier.value); cleanEntryControllers(entryNotifier.value);
_videoActionDelegate.dispose(); _videoActionDelegate.dispose();
_verticalPageAnimationController.dispose();
_overlayAnimationController.dispose(); _overlayAnimationController.dispose();
_overlayVisible.dispose(); _overlayVisible.dispose();
_viewLocked.dispose(); _viewLocked.dispose();
@ -606,14 +610,11 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
} }
Future<void> _goToVerticalPage(int page) async { Future<void> _goToVerticalPage(int page) async {
final animationDuration = context.read<DurationsData>().viewerVerticalPageScrollAnimation; if (settings.accessibilityAnimations.animate) {
if (animationDuration > Duration.zero) { final start = _verticalPager.offset;
// duration & curve should feel similar to changing page by vertical fling final end = _verticalPager.position.viewportDimension * page;
await _verticalPager.animateToPage( final simulation = ScrollSpringSimulation(ViewerVerticalPageView.spring, start, end, 0);
page, unawaited(_verticalPageAnimationController.animateWith(simulation));
duration: animationDuration,
curve: Curves.easeOutQuart,
);
} else { } else {
_verticalPager.jumpToPage(page); _verticalPager.jumpToPage(page);
} }
@ -653,13 +654,13 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
page = page.clamp(0, _collection.entryCount - 1); page = page.clamp(0, _collection.entryCount - 1);
} }
if (_currentEntryIndex != page) { if (_currentEntryIndex != page) {
final animationDuration = animate ? context.read<DurationsData>().viewerVerticalPageScrollAnimation : Duration.zero; final animationDuration = animate ? context.read<DurationsData>().viewerHorizontalPageScrollAnimation : Duration.zero;
if (animationDuration > Duration.zero) { if (animationDuration > Duration.zero) {
// duration & curve should feel similar to changing page by vertical fling // duration & curve should feel similar to changing page by fling
await _horizontalPager.animateToPage( await _horizontalPager.animateToPage(
page, page,
duration: animationDuration, duration: animationDuration,
curve: Curves.easeOutQuart, curve: Curves.easeOutCubic,
); );
} else { } else {
_horizontalPager.jumpToPage(page); _horizontalPager.jumpToPage(page);

View file

@ -124,11 +124,10 @@ class _InfoPageState extends State<InfoPage> {
} }
void _goToViewer() { void _goToViewer() {
final animationDuration = context.read<DurationsData>().viewerVerticalPageScrollAnimation;
ShowImageNotification().dispatch(context); ShowImageNotification().dispatch(context);
_scrollController.animateTo( _scrollController.animateTo(
0, 0,
duration: animationDuration, duration: Durations.pageTransitionAnimation,
curve: Curves.easeInOut, curve: Curves.easeInOut,
); );
} }

View file

@ -17,8 +17,8 @@ class MagnifierScrollerPhysics extends ScrollPhysics {
const MagnifierScrollerPhysics({ const MagnifierScrollerPhysics({
required this.gestureSettings, required this.gestureSettings,
this.touchSlopFactor = 1, this.touchSlopFactor = 1,
ScrollPhysics? parent, super.parent,
}) : super(parent: parent); });
@override @override
MagnifierScrollerPhysics applyTo(ScrollPhysics? ancestor) { MagnifierScrollerPhysics applyTo(ScrollPhysics? ancestor) {