#290 slideshow: animated zoom effect
This commit is contained in:
parent
1fa68f082f
commit
6df9456372
17 changed files with 115 additions and 18 deletions
|
@ -14,7 +14,8 @@ All notable changes to this project will be documented in this file.
|
||||||
- Stats: top albums
|
- Stats: top albums
|
||||||
- Stats: open full top listings
|
- Stats: open full top listings
|
||||||
- Video: option for muted auto play
|
- Video: option for muted auto play
|
||||||
- Slideshow: option for no transition
|
- Slideshow / Screen saver: option for no transition
|
||||||
|
- Slideshow / Screen saver: animated zoom effect
|
||||||
- Widget: tap action setting
|
- Widget: tap action setting
|
||||||
- Wallpaper: scroll effect option
|
- Wallpaper: scroll effect option
|
||||||
|
|
||||||
|
|
|
@ -715,6 +715,7 @@
|
||||||
"settingsSlideshowRepeat": "Repeat",
|
"settingsSlideshowRepeat": "Repeat",
|
||||||
"settingsSlideshowShuffle": "Shuffle",
|
"settingsSlideshowShuffle": "Shuffle",
|
||||||
"settingsSlideshowFillScreen": "Fill screen",
|
"settingsSlideshowFillScreen": "Fill screen",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect": "Animated zoom effect",
|
||||||
"settingsSlideshowTransitionTile": "Transition",
|
"settingsSlideshowTransitionTile": "Transition",
|
||||||
"settingsSlideshowTransitionDialogTitle": "Transition",
|
"settingsSlideshowTransitionDialogTitle": "Transition",
|
||||||
"settingsSlideshowIntervalTile": "Interval",
|
"settingsSlideshowIntervalTile": "Interval",
|
||||||
|
|
|
@ -534,6 +534,7 @@
|
||||||
"settingsSlideshowRepeat": "Répéter",
|
"settingsSlideshowRepeat": "Répéter",
|
||||||
"settingsSlideshowShuffle": "Aléatoire",
|
"settingsSlideshowShuffle": "Aléatoire",
|
||||||
"settingsSlideshowFillScreen": "Remplir l’écran",
|
"settingsSlideshowFillScreen": "Remplir l’écran",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect": "Effet de zoom animé",
|
||||||
"settingsSlideshowTransitionTile": "Transition",
|
"settingsSlideshowTransitionTile": "Transition",
|
||||||
"settingsSlideshowTransitionDialogTitle": "Transition",
|
"settingsSlideshowTransitionDialogTitle": "Transition",
|
||||||
"settingsSlideshowIntervalTile": "Intervalle",
|
"settingsSlideshowIntervalTile": "Intervalle",
|
||||||
|
|
|
@ -534,6 +534,7 @@
|
||||||
"settingsSlideshowRepeat": "반복",
|
"settingsSlideshowRepeat": "반복",
|
||||||
"settingsSlideshowShuffle": "순서섞기",
|
"settingsSlideshowShuffle": "순서섞기",
|
||||||
"settingsSlideshowFillScreen": "화면 채우기",
|
"settingsSlideshowFillScreen": "화면 채우기",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect": "애니메이션 확대/축소 효과",
|
||||||
"settingsSlideshowTransitionTile": "전환 효과",
|
"settingsSlideshowTransitionTile": "전환 효과",
|
||||||
"settingsSlideshowTransitionDialogTitle": "전환 효과",
|
"settingsSlideshowTransitionDialogTitle": "전환 효과",
|
||||||
"settingsSlideshowIntervalTile": "교체 주기",
|
"settingsSlideshowIntervalTile": "교체 주기",
|
||||||
|
|
|
@ -129,6 +129,7 @@ class SettingsDefaults {
|
||||||
static const slideshowRepeat = false;
|
static const slideshowRepeat = false;
|
||||||
static const slideshowShuffle = false;
|
static const slideshowShuffle = false;
|
||||||
static const slideshowFillScreen = false;
|
static const slideshowFillScreen = false;
|
||||||
|
static const slideshowAnimatedZoomEffect = true;
|
||||||
static const slideshowTransition = ViewerTransition.fade;
|
static const slideshowTransition = ViewerTransition.fade;
|
||||||
static const slideshowVideoPlayback = SlideshowVideoPlayback.playMuted;
|
static const slideshowVideoPlayback = SlideshowVideoPlayback.playMuted;
|
||||||
static const slideshowInterval = SlideshowInterval.s5;
|
static const slideshowInterval = SlideshowInterval.s5;
|
||||||
|
|
|
@ -151,6 +151,7 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
// screen saver
|
// screen saver
|
||||||
static const screenSaverFillScreenKey = 'screen_saver_fill_screen';
|
static const screenSaverFillScreenKey = 'screen_saver_fill_screen';
|
||||||
|
static const screenSaverAnimatedZoomEffectKey = 'screen_saver_animated_zoom_effect';
|
||||||
static const screenSaverTransitionKey = 'screen_saver_transition';
|
static const screenSaverTransitionKey = 'screen_saver_transition';
|
||||||
static const screenSaverVideoPlaybackKey = 'screen_saver_video_playback';
|
static const screenSaverVideoPlaybackKey = 'screen_saver_video_playback';
|
||||||
static const screenSaverIntervalKey = 'screen_saver_interval';
|
static const screenSaverIntervalKey = 'screen_saver_interval';
|
||||||
|
@ -160,6 +161,7 @@ class Settings extends ChangeNotifier {
|
||||||
static const slideshowRepeatKey = 'slideshow_loop';
|
static const slideshowRepeatKey = 'slideshow_loop';
|
||||||
static const slideshowShuffleKey = 'slideshow_shuffle';
|
static const slideshowShuffleKey = 'slideshow_shuffle';
|
||||||
static const slideshowFillScreenKey = 'slideshow_fill_screen';
|
static const slideshowFillScreenKey = 'slideshow_fill_screen';
|
||||||
|
static const slideshowAnimatedZoomEffectKey = 'slideshow_animated_zoom_effect';
|
||||||
static const slideshowTransitionKey = 'slideshow_transition';
|
static const slideshowTransitionKey = 'slideshow_transition';
|
||||||
static const slideshowVideoPlaybackKey = 'slideshow_video_playback';
|
static const slideshowVideoPlaybackKey = 'slideshow_video_playback';
|
||||||
static const slideshowIntervalKey = 'slideshow_interval';
|
static const slideshowIntervalKey = 'slideshow_interval';
|
||||||
|
@ -649,6 +651,10 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
set screenSaverFillScreen(bool newValue) => setAndNotify(screenSaverFillScreenKey, newValue);
|
set screenSaverFillScreen(bool newValue) => setAndNotify(screenSaverFillScreenKey, newValue);
|
||||||
|
|
||||||
|
bool get screenSaverAnimatedZoomEffect => getBoolOrDefault(screenSaverAnimatedZoomEffectKey, SettingsDefaults.slideshowAnimatedZoomEffect);
|
||||||
|
|
||||||
|
set screenSaverAnimatedZoomEffect(bool newValue) => setAndNotify(screenSaverAnimatedZoomEffectKey, newValue);
|
||||||
|
|
||||||
ViewerTransition get screenSaverTransition => getEnumOrDefault(screenSaverTransitionKey, SettingsDefaults.slideshowTransition, ViewerTransition.values);
|
ViewerTransition get screenSaverTransition => getEnumOrDefault(screenSaverTransitionKey, SettingsDefaults.slideshowTransition, ViewerTransition.values);
|
||||||
|
|
||||||
set screenSaverTransition(ViewerTransition newValue) => setAndNotify(screenSaverTransitionKey, newValue.toString());
|
set screenSaverTransition(ViewerTransition newValue) => setAndNotify(screenSaverTransitionKey, newValue.toString());
|
||||||
|
@ -679,6 +685,10 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
set slideshowFillScreen(bool newValue) => setAndNotify(slideshowFillScreenKey, newValue);
|
set slideshowFillScreen(bool newValue) => setAndNotify(slideshowFillScreenKey, newValue);
|
||||||
|
|
||||||
|
bool get slideshowAnimatedZoomEffect => getBoolOrDefault(slideshowAnimatedZoomEffectKey, SettingsDefaults.slideshowAnimatedZoomEffect);
|
||||||
|
|
||||||
|
set slideshowAnimatedZoomEffect(bool newValue) => setAndNotify(slideshowAnimatedZoomEffectKey, newValue);
|
||||||
|
|
||||||
ViewerTransition get slideshowTransition => getEnumOrDefault(slideshowTransitionKey, SettingsDefaults.slideshowTransition, ViewerTransition.values);
|
ViewerTransition get slideshowTransition => getEnumOrDefault(slideshowTransitionKey, SettingsDefaults.slideshowTransition, ViewerTransition.values);
|
||||||
|
|
||||||
set slideshowTransition(ViewerTransition newValue) => setAndNotify(slideshowTransitionKey, newValue.toString());
|
set slideshowTransition(ViewerTransition newValue) => setAndNotify(slideshowTransitionKey, newValue.toString());
|
||||||
|
@ -880,9 +890,11 @@ class Settings extends ChangeNotifier {
|
||||||
case saveSearchHistoryKey:
|
case saveSearchHistoryKey:
|
||||||
case filePickerShowHiddenFilesKey:
|
case filePickerShowHiddenFilesKey:
|
||||||
case screenSaverFillScreenKey:
|
case screenSaverFillScreenKey:
|
||||||
|
case screenSaverAnimatedZoomEffectKey:
|
||||||
case slideshowRepeatKey:
|
case slideshowRepeatKey:
|
||||||
case slideshowShuffleKey:
|
case slideshowShuffleKey:
|
||||||
case slideshowFillScreenKey:
|
case slideshowFillScreenKey:
|
||||||
|
case slideshowAnimatedZoomEffectKey:
|
||||||
if (newValue is bool) {
|
if (newValue is bool) {
|
||||||
settingsStore.setBool(key, newValue);
|
settingsStore.setBool(key, newValue);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -41,6 +41,7 @@ class Durations {
|
||||||
static const thumbnailScrollerShadeAnimation = Duration(milliseconds: 150);
|
static const thumbnailScrollerShadeAnimation = Duration(milliseconds: 150);
|
||||||
static const viewerVideoPlayerTransition = Duration(milliseconds: 500);
|
static const viewerVideoPlayerTransition = Duration(milliseconds: 500);
|
||||||
static const viewerActionFeedbackAnimation = Duration(milliseconds: 600);
|
static const viewerActionFeedbackAnimation = Duration(milliseconds: 600);
|
||||||
|
static const viewerHorizontalPageAnimation = Duration(seconds: 1);
|
||||||
|
|
||||||
// info animations
|
// info animations
|
||||||
static const mapStyleSwitchAnimation = Duration(milliseconds: 300);
|
static const mapStyleSwitchAnimation = Duration(milliseconds: 300);
|
||||||
|
|
|
@ -45,7 +45,7 @@ class ScaleBoundaries extends Equatable {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
double _scaleForLevel(ScaleLevel level) {
|
double scaleForLevel(ScaleLevel level) {
|
||||||
final factor = level.factor;
|
final factor = level.factor;
|
||||||
switch (level.ref) {
|
switch (level.ref) {
|
||||||
case ScaleReference.contained:
|
case ScaleReference.contained:
|
||||||
|
@ -61,18 +61,18 @@ class ScaleBoundaries extends Equatable {
|
||||||
double get originalScale => 1.0 / window.devicePixelRatio;
|
double get originalScale => 1.0 / window.devicePixelRatio;
|
||||||
|
|
||||||
double get minScale => {
|
double get minScale => {
|
||||||
_scaleForLevel(_minScale),
|
scaleForLevel(_minScale),
|
||||||
_allowOriginalScaleBeyondRange ? originalScale : double.infinity,
|
_allowOriginalScaleBeyondRange ? originalScale : double.infinity,
|
||||||
initialScale,
|
initialScale,
|
||||||
}.fold(double.infinity, min);
|
}.fold(double.infinity, min);
|
||||||
|
|
||||||
double get maxScale => {
|
double get maxScale => {
|
||||||
_scaleForLevel(_maxScale),
|
scaleForLevel(_maxScale),
|
||||||
_allowOriginalScaleBeyondRange ? originalScale : double.negativeInfinity,
|
_allowOriginalScaleBeyondRange ? originalScale : double.negativeInfinity,
|
||||||
initialScale,
|
initialScale,
|
||||||
}.fold(0, max);
|
}.fold(0, max);
|
||||||
|
|
||||||
double get initialScale => _scaleForLevel(_initialScale);
|
double get initialScale => scaleForLevel(_initialScale);
|
||||||
|
|
||||||
Offset get _viewportCenter => viewportSize.center(Offset.zero);
|
Offset get _viewportCenter => viewportSize.center(Offset.zero);
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,11 @@ class ScreenSaverSettingsPage extends StatelessWidget {
|
||||||
onChanged: (v) => settings.screenSaverFillScreen = v,
|
onChanged: (v) => settings.screenSaverFillScreen = v,
|
||||||
title: context.l10n.settingsSlideshowFillScreen,
|
title: context.l10n.settingsSlideshowFillScreen,
|
||||||
),
|
),
|
||||||
|
SettingsSwitchListTile(
|
||||||
|
selector: (context, s) => s.screenSaverAnimatedZoomEffect,
|
||||||
|
onChanged: (v) => settings.screenSaverAnimatedZoomEffect = v,
|
||||||
|
title: context.l10n.settingsSlideshowAnimatedZoomEffect,
|
||||||
|
),
|
||||||
SettingsSelectionListTile<ViewerTransition>(
|
SettingsSelectionListTile<ViewerTransition>(
|
||||||
values: ViewerTransition.values,
|
values: ViewerTransition.values,
|
||||||
getName: (context, v) => v.getName(context),
|
getName: (context, v) => v.getName(context),
|
||||||
|
|
|
@ -36,6 +36,11 @@ class ViewerSlideshowPage extends StatelessWidget {
|
||||||
onChanged: (v) => settings.slideshowFillScreen = v,
|
onChanged: (v) => settings.slideshowFillScreen = v,
|
||||||
title: context.l10n.settingsSlideshowFillScreen,
|
title: context.l10n.settingsSlideshowFillScreen,
|
||||||
),
|
),
|
||||||
|
SettingsSwitchListTile(
|
||||||
|
selector: (context, s) => s.slideshowAnimatedZoomEffect,
|
||||||
|
onChanged: (v) => settings.slideshowAnimatedZoomEffect = v,
|
||||||
|
title: context.l10n.settingsSlideshowAnimatedZoomEffect,
|
||||||
|
),
|
||||||
SettingsSelectionListTile<ViewerTransition>(
|
SettingsSelectionListTile<ViewerTransition>(
|
||||||
values: ViewerTransition.values,
|
values: ViewerTransition.values,
|
||||||
getName: (context, v) => v.getName(context),
|
getName: (context, v) => v.getName(context),
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/settings/enums/enums.dart';
|
import 'package:aves/model/settings/enums/enums.dart';
|
||||||
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/widgets/common/magnifier/scale/scale_level.dart';
|
import 'package:aves/widgets/common/magnifier/scale/scale_level.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class ViewerController {
|
class ViewerController {
|
||||||
final ValueNotifier<AvesEntry?> entryNotifier = ValueNotifier(null);
|
final ValueNotifier<AvesEntry?> entryNotifier = ValueNotifier(null);
|
||||||
final ScaleLevel initialScale;
|
|
||||||
final ViewerTransition transition;
|
final ViewerTransition transition;
|
||||||
final Duration? autopilotInterval;
|
final Duration? autopilotInterval;
|
||||||
|
final bool autopilotAnimatedZoom;
|
||||||
final bool repeat;
|
final bool repeat;
|
||||||
|
|
||||||
|
late final ScaleLevel _initialScale;
|
||||||
late final ValueNotifier<bool> _autopilotNotifier;
|
late final ValueNotifier<bool> _autopilotNotifier;
|
||||||
Timer? _playTimer;
|
Timer? _playTimer;
|
||||||
final StreamController _streamController = StreamController.broadcast();
|
final StreamController _streamController = StreamController.broadcast();
|
||||||
|
final Map<TickerProvider, AnimationController> _autopilotAnimationControllers = {};
|
||||||
|
ScaleLevel? _autopilotInitialScale;
|
||||||
|
|
||||||
Stream<dynamic> get _events => _streamController.stream;
|
Stream<dynamic> get _events => _streamController.stream;
|
||||||
|
|
||||||
|
@ -26,13 +31,22 @@ class ViewerController {
|
||||||
|
|
||||||
set autopilot(bool enabled) => _autopilotNotifier.value = enabled;
|
set autopilot(bool enabled) => _autopilotNotifier.value = enabled;
|
||||||
|
|
||||||
|
ScaleLevel get initialScale => _autopilotInitialScale ?? _initialScale;
|
||||||
|
|
||||||
|
static final _autopilotScaleTweens = [
|
||||||
|
Tween<double>(begin: 1, end: 1.2),
|
||||||
|
Tween<double>(begin: 1.2, end: 1),
|
||||||
|
];
|
||||||
|
|
||||||
ViewerController({
|
ViewerController({
|
||||||
this.initialScale = const ScaleLevel(ref: ScaleReference.contained),
|
ScaleLevel initialScale = const ScaleLevel(ref: ScaleReference.contained),
|
||||||
this.transition = ViewerTransition.parallax,
|
this.transition = ViewerTransition.parallax,
|
||||||
this.repeat = false,
|
this.repeat = false,
|
||||||
bool autopilot = false,
|
bool autopilot = false,
|
||||||
this.autopilotInterval,
|
this.autopilotInterval,
|
||||||
|
this.autopilotAnimatedZoom = false,
|
||||||
}) {
|
}) {
|
||||||
|
_initialScale = initialScale;
|
||||||
_autopilotNotifier = ValueNotifier(autopilot);
|
_autopilotNotifier = ValueNotifier(autopilot);
|
||||||
_autopilotNotifier.addListener(_onAutopilotChange);
|
_autopilotNotifier.addListener(_onAutopilotChange);
|
||||||
_onAutopilotChange();
|
_onAutopilotChange();
|
||||||
|
@ -40,21 +54,53 @@ class ViewerController {
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_autopilotNotifier.removeListener(_onAutopilotChange);
|
_autopilotNotifier.removeListener(_onAutopilotChange);
|
||||||
|
_clearAutopilotAnimations();
|
||||||
_stopPlayTimer();
|
_stopPlayTimer();
|
||||||
_streamController.close();
|
_streamController.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _stopPlayTimer() {
|
|
||||||
_playTimer?.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onAutopilotChange() {
|
void _onAutopilotChange() {
|
||||||
|
_clearAutopilotAnimations();
|
||||||
_stopPlayTimer();
|
_stopPlayTimer();
|
||||||
if (autopilot && autopilotInterval != null) {
|
if (autopilot && autopilotInterval != null) {
|
||||||
_playTimer = Timer.periodic(autopilotInterval!, (_) => _streamController.add(ViewerShowNextEvent()));
|
_playTimer = Timer.periodic(autopilotInterval!, (_) => _streamController.add(ViewerShowNextEvent()));
|
||||||
_streamController.add(const ViewerOverlayToggleEvent(visible: false));
|
_streamController.add(const ViewerOverlayToggleEvent(visible: false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _stopPlayTimer() => _playTimer?.cancel();
|
||||||
|
|
||||||
|
void _clearAutopilotAnimations() => _autopilotAnimationControllers.keys.toSet().forEach((v) => stopAutopilotAnimation(vsync: v));
|
||||||
|
|
||||||
|
void stopAutopilotAnimation({required TickerProvider vsync}) => _autopilotAnimationControllers.remove(vsync)?.dispose();
|
||||||
|
|
||||||
|
void startAutopilotAnimation({
|
||||||
|
required TickerProvider vsync,
|
||||||
|
required void Function({required ScaleLevel scaleLevel}) onUpdate,
|
||||||
|
}) {
|
||||||
|
stopAutopilotAnimation(vsync: vsync);
|
||||||
|
if (!autopilot || !autopilotAnimatedZoom) return;
|
||||||
|
|
||||||
|
final scaleLevelRef = _initialScale.ref;
|
||||||
|
final scaleFactorTween = _autopilotScaleTweens[Random().nextInt(_autopilotScaleTweens.length)];
|
||||||
|
_autopilotInitialScale = ScaleLevel(ref: scaleLevelRef, factor: scaleFactorTween.begin!);
|
||||||
|
|
||||||
|
final animationController = AnimationController(
|
||||||
|
duration: autopilotInterval,
|
||||||
|
vsync: vsync,
|
||||||
|
);
|
||||||
|
animationController.addListener(() => onUpdate.call(
|
||||||
|
scaleLevel: ScaleLevel(
|
||||||
|
ref: scaleLevelRef,
|
||||||
|
factor: scaleFactorTween.evaluate(CurvedAnimation(
|
||||||
|
parent: animationController,
|
||||||
|
curve: Curves.linear,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
_autopilotAnimationControllers[vsync] = animationController;
|
||||||
|
Future.delayed(Durations.viewerHorizontalPageAnimation).then((_) => _autopilotAnimationControllers[vsync]?.forward());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
|
|
|
@ -90,7 +90,7 @@ class _MultiEntryScrollerState extends State<MultiEntryScroller> with AutomaticK
|
||||||
key: const Key('image_view'),
|
key: const Key('image_view'),
|
||||||
mainEntry: mainEntry,
|
mainEntry: mainEntry,
|
||||||
pageEntry: pageEntry ?? mainEntry,
|
pageEntry: pageEntry ?? mainEntry,
|
||||||
initialScale: viewerController.initialScale,
|
viewerController: viewerController,
|
||||||
onDisposed: () => widget.onViewDisposed(mainEntry, pageEntry),
|
onDisposed: () => widget.onViewDisposed(mainEntry, pageEntry),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ class _SingleEntryScrollerState extends State<SingleEntryScroller> with Automati
|
||||||
return EntryPageView(
|
return EntryPageView(
|
||||||
mainEntry: mainEntry,
|
mainEntry: mainEntry,
|
||||||
pageEntry: pageEntry ?? mainEntry,
|
pageEntry: pageEntry ?? mainEntry,
|
||||||
initialScale: viewerController.initialScale,
|
viewerController: widget.viewerController,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -229,7 +229,7 @@ class _ViewerVerticalPageViewState extends State<ViewerVerticalPageView> {
|
||||||
if (animate) {
|
if (animate) {
|
||||||
pageController.animateToPage(
|
pageController.animateToPage(
|
||||||
target,
|
target,
|
||||||
duration: const Duration(seconds: 1),
|
duration: Durations.viewerHorizontalPageAnimation,
|
||||||
curve: Curves.easeInOutCubic,
|
curve: Curves.easeInOutCubic,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -43,6 +43,7 @@ class _ScreenSaverPageState extends State<ScreenSaverPage> with WidgetsBindingOb
|
||||||
repeat: true,
|
repeat: true,
|
||||||
autopilot: true,
|
autopilot: true,
|
||||||
autopilotInterval: settings.screenSaverInterval.getDuration(),
|
autopilotInterval: settings.screenSaverInterval.getDuration(),
|
||||||
|
autopilotAnimatedZoom: settings.screenSaverAnimatedZoomEffect,
|
||||||
);
|
);
|
||||||
source.stateNotifier.addListener(_onSourceStateChanged);
|
source.stateNotifier.addListener(_onSourceStateChanged);
|
||||||
_initSlideshowCollection();
|
_initSlideshowCollection();
|
||||||
|
|
|
@ -45,6 +45,7 @@ class _SlideshowPageState extends State<SlideshowPage> {
|
||||||
repeat: settings.slideshowRepeat,
|
repeat: settings.slideshowRepeat,
|
||||||
autopilot: true,
|
autopilot: true,
|
||||||
autopilotInterval: settings.slideshowInterval.getDuration(),
|
autopilotInterval: settings.slideshowInterval.getDuration(),
|
||||||
|
autopilotAnimatedZoom: settings.slideshowAnimatedZoomEffect,
|
||||||
);
|
);
|
||||||
_initSlideshowCollection();
|
_initSlideshowCollection();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'package:aves/widgets/common/magnifier/scale/scale_boundaries.dart';
|
||||||
import 'package:aves/widgets/common/magnifier/scale/scale_level.dart';
|
import 'package:aves/widgets/common/magnifier/scale/scale_level.dart';
|
||||||
import 'package:aves/widgets/common/magnifier/scale/state.dart';
|
import 'package:aves/widgets/common/magnifier/scale/state.dart';
|
||||||
import 'package:aves/widgets/common/thumbnail/image.dart';
|
import 'package:aves/widgets/common/thumbnail/image.dart';
|
||||||
|
import 'package:aves/widgets/viewer/controller.dart';
|
||||||
import 'package:aves/widgets/viewer/hero.dart';
|
import 'package:aves/widgets/viewer/hero.dart';
|
||||||
import 'package:aves/widgets/viewer/notifications.dart';
|
import 'package:aves/widgets/viewer/notifications.dart';
|
||||||
import 'package:aves/widgets/viewer/video/conductor.dart';
|
import 'package:aves/widgets/viewer/video/conductor.dart';
|
||||||
|
@ -35,7 +36,7 @@ import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
class EntryPageView extends StatefulWidget {
|
class EntryPageView extends StatefulWidget {
|
||||||
final AvesEntry mainEntry, pageEntry;
|
final AvesEntry mainEntry, pageEntry;
|
||||||
final ScaleLevel initialScale;
|
final ViewerController viewerController;
|
||||||
final VoidCallback? onDisposed;
|
final VoidCallback? onDisposed;
|
||||||
|
|
||||||
static const decorationCheckSize = 20.0;
|
static const decorationCheckSize = 20.0;
|
||||||
|
@ -44,7 +45,7 @@ class EntryPageView extends StatefulWidget {
|
||||||
super.key,
|
super.key,
|
||||||
required this.mainEntry,
|
required this.mainEntry,
|
||||||
required this.pageEntry,
|
required this.pageEntry,
|
||||||
required this.initialScale,
|
required this.viewerController,
|
||||||
this.onDisposed,
|
this.onDisposed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ class EntryPageView extends StatefulWidget {
|
||||||
State<EntryPageView> createState() => _EntryPageViewState();
|
State<EntryPageView> createState() => _EntryPageViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EntryPageViewState extends State<EntryPageView> {
|
class _EntryPageViewState extends State<EntryPageView> with SingleTickerProviderStateMixin {
|
||||||
late ValueNotifier<ViewState> _viewStateNotifier;
|
late ValueNotifier<ViewState> _viewStateNotifier;
|
||||||
late MagnifierController _magnifierController;
|
late MagnifierController _magnifierController;
|
||||||
final List<StreamSubscription> _subscriptions = [];
|
final List<StreamSubscription> _subscriptions = [];
|
||||||
|
@ -72,6 +73,8 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
|
|
||||||
AvesEntry get entry => widget.pageEntry;
|
AvesEntry get entry => widget.pageEntry;
|
||||||
|
|
||||||
|
ViewerController get viewerController => widget.viewerController;
|
||||||
|
|
||||||
// use the high res photo as cover for the video part of a motion photo
|
// use the high res photo as cover for the video part of a motion photo
|
||||||
ImageProvider get videoCoverUriImage => mainEntry.isMotionPhoto ? mainEntry.uriImage : entry.uriImage;
|
ImageProvider get videoCoverUriImage => mainEntry.isMotionPhoto ? mainEntry.uriImage : entry.uriImage;
|
||||||
|
|
||||||
|
@ -112,9 +115,16 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
_videoCoverStream = videoCoverUriImage.resolve(ImageConfiguration.empty);
|
_videoCoverStream = videoCoverUriImage.resolve(ImageConfiguration.empty);
|
||||||
_videoCoverStream!.addListener(_videoCoverStreamListener);
|
_videoCoverStream!.addListener(_videoCoverStreamListener);
|
||||||
}
|
}
|
||||||
|
viewerController.startAutopilotAnimation(
|
||||||
|
vsync: this,
|
||||||
|
onUpdate: ({required scaleLevel}) {
|
||||||
|
final scale = _magnifierController.scaleBoundaries.scaleForLevel(scaleLevel);
|
||||||
|
_magnifierController.update(scale: scale, source: ChangeSource.animation);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _unregisterWidget(EntryPageView oldWidget) {
|
void _unregisterWidget(EntryPageView oldWidget) {
|
||||||
|
viewerController.stopAutopilotAnimation(vsync: this);
|
||||||
_videoCoverStream?.removeListener(_videoCoverStreamListener);
|
_videoCoverStream?.removeListener(_videoCoverStreamListener);
|
||||||
_videoCoverStream = null;
|
_videoCoverStream = null;
|
||||||
_videoCoverInfoNotifier.value = null;
|
_videoCoverInfoNotifier.value = null;
|
||||||
|
@ -385,7 +395,7 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
allowOriginalScaleBeyondRange: !isWallpaperMode,
|
allowOriginalScaleBeyondRange: !isWallpaperMode,
|
||||||
minScale: minScale,
|
minScale: minScale,
|
||||||
maxScale: maxScale,
|
maxScale: maxScale,
|
||||||
initialScale: widget.initialScale,
|
initialScale: viewerController.initialScale,
|
||||||
scaleStateCycle: scaleStateCycle,
|
scaleStateCycle: scaleStateCycle,
|
||||||
applyScale: applyScale,
|
applyScale: applyScale,
|
||||||
onTap: (c, s, a, p) => _onTap(alignment: a),
|
onTap: (c, s, a, p) => _onTap(alignment: a),
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
"albumGroupType",
|
"albumGroupType",
|
||||||
"albumMimeTypeMixed",
|
"albumMimeTypeMixed",
|
||||||
"settingsDisabled",
|
"settingsDisabled",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect"
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
"albumGroupType",
|
"albumGroupType",
|
||||||
"albumMimeTypeMixed",
|
"albumMimeTypeMixed",
|
||||||
"settingsDisabled",
|
"settingsDisabled",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect"
|
||||||
|
@ -54,6 +56,7 @@
|
||||||
"searchMetadataSectionTitle",
|
"searchMetadataSectionTitle",
|
||||||
"settingsDisabled",
|
"settingsDisabled",
|
||||||
"settingsConfirmationAfterMoveToBinItems",
|
"settingsConfirmationAfterMoveToBinItems",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"viewerInfoLabelDescription",
|
"viewerInfoLabelDescription",
|
||||||
|
@ -74,6 +77,7 @@
|
||||||
"albumGroupType",
|
"albumGroupType",
|
||||||
"albumMimeTypeMixed",
|
"albumMimeTypeMixed",
|
||||||
"settingsDisabled",
|
"settingsDisabled",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect"
|
||||||
|
@ -89,6 +93,7 @@
|
||||||
"albumGroupType",
|
"albumGroupType",
|
||||||
"albumMimeTypeMixed",
|
"albumMimeTypeMixed",
|
||||||
"settingsDisabled",
|
"settingsDisabled",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect"
|
||||||
|
@ -120,6 +125,7 @@
|
||||||
"settingsDisabled",
|
"settingsDisabled",
|
||||||
"settingsConfirmationAfterMoveToBinItems",
|
"settingsConfirmationAfterMoveToBinItems",
|
||||||
"settingsViewerGestureSideTapNext",
|
"settingsViewerGestureSideTapNext",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"viewerInfoLabelDescription",
|
"viewerInfoLabelDescription",
|
||||||
|
@ -140,6 +146,7 @@
|
||||||
"albumGroupType",
|
"albumGroupType",
|
||||||
"albumMimeTypeMixed",
|
"albumMimeTypeMixed",
|
||||||
"settingsDisabled",
|
"settingsDisabled",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect"
|
||||||
|
@ -155,6 +162,7 @@
|
||||||
"albumGroupType",
|
"albumGroupType",
|
||||||
"albumMimeTypeMixed",
|
"albumMimeTypeMixed",
|
||||||
"settingsDisabled",
|
"settingsDisabled",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect"
|
||||||
|
@ -170,6 +178,7 @@
|
||||||
"albumGroupType",
|
"albumGroupType",
|
||||||
"albumMimeTypeMixed",
|
"albumMimeTypeMixed",
|
||||||
"settingsDisabled",
|
"settingsDisabled",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect"
|
||||||
|
@ -220,6 +229,7 @@
|
||||||
"settingsSlideshowRepeat",
|
"settingsSlideshowRepeat",
|
||||||
"settingsSlideshowShuffle",
|
"settingsSlideshowShuffle",
|
||||||
"settingsSlideshowFillScreen",
|
"settingsSlideshowFillScreen",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsSlideshowTransitionTile",
|
"settingsSlideshowTransitionTile",
|
||||||
"settingsSlideshowTransitionDialogTitle",
|
"settingsSlideshowTransitionDialogTitle",
|
||||||
"settingsSlideshowIntervalTile",
|
"settingsSlideshowIntervalTile",
|
||||||
|
@ -245,6 +255,7 @@
|
||||||
"albumGroupType",
|
"albumGroupType",
|
||||||
"albumMimeTypeMixed",
|
"albumMimeTypeMixed",
|
||||||
"settingsDisabled",
|
"settingsDisabled",
|
||||||
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect"
|
||||||
|
|
Loading…
Reference in a new issue