mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-01 08:38:18 +08:00
@@ -97,8 +97,7 @@ class CustomGridView extends StatelessWidget {
|
|||||||
liveUrl: isLive ? item.liveUrl : null,
|
liveUrl: isLive ? item.liveUrl : null,
|
||||||
width: isLive ? item.width.toInt() : null,
|
width: isLive ? item.width.toInt() : null,
|
||||||
height: isLive ? item.height.toInt() : null,
|
height: isLive ? item.height.toInt() : null,
|
||||||
// width: item.width.toInt(),
|
isLongPic: item.isLongPic,
|
||||||
// height: item.height.toInt(),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).toList();
|
).toList();
|
||||||
@@ -258,6 +257,40 @@ class CustomGridView extends StatelessWidget {
|
|||||||
children: List.generate(length, (index) {
|
children: List.generate(length, (index) {
|
||||||
final item = picArr[index];
|
final item = picArr[index];
|
||||||
final borderRadius = _borderRadius(column, length, index);
|
final borderRadius = _borderRadius(column, length, index);
|
||||||
|
Widget child = Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
src: item.url,
|
||||||
|
width: imageWidth,
|
||||||
|
height: imageHeight,
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
alignment: item.isLongPic ? .topCenter : .center,
|
||||||
|
cacheWidth: item.width <= item.height,
|
||||||
|
getPlaceHolder: () => placeHolder,
|
||||||
|
),
|
||||||
|
if (item.isLivePhoto)
|
||||||
|
const PBadge(
|
||||||
|
text: 'Live',
|
||||||
|
right: 8,
|
||||||
|
bottom: 8,
|
||||||
|
type: PBadgeType.gray,
|
||||||
|
)
|
||||||
|
else if (item.isLongPic)
|
||||||
|
const PBadge(
|
||||||
|
text: '长图',
|
||||||
|
right: 8,
|
||||||
|
bottom: 8,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
if (!item.isLongPic) {
|
||||||
|
child = Hero(
|
||||||
|
tag: item.url,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
return LayoutId(
|
return LayoutId(
|
||||||
id: index,
|
id: index,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@@ -270,37 +303,7 @@ class CustomGridView extends StatelessWidget {
|
|||||||
? (details) =>
|
? (details) =>
|
||||||
_showMenu(context, details.globalPosition, item)
|
_showMenu(context, details.globalPosition, item)
|
||||||
: null,
|
: null,
|
||||||
child: Hero(
|
child: child,
|
||||||
tag: item.url,
|
|
||||||
child: Stack(
|
|
||||||
clipBehavior: Clip.none,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
NetworkImgLayer(
|
|
||||||
src: item.url,
|
|
||||||
width: imageWidth,
|
|
||||||
height: imageHeight,
|
|
||||||
borderRadius: borderRadius,
|
|
||||||
alignment: item.isLongPic ? .topCenter : .center,
|
|
||||||
cacheWidth: item.width <= item.height,
|
|
||||||
getPlaceHolder: () => placeHolder,
|
|
||||||
),
|
|
||||||
if (item.isLivePhoto)
|
|
||||||
const PBadge(
|
|
||||||
text: 'Live',
|
|
||||||
right: 8,
|
|
||||||
bottom: 8,
|
|
||||||
type: PBadgeType.gray,
|
|
||||||
)
|
|
||||||
else if (item.isLongPic)
|
|
||||||
const PBadge(
|
|
||||||
text: '长图',
|
|
||||||
right: 8,
|
|
||||||
bottom: 8,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -167,8 +167,6 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAnimating() => _animateController.value != 0;
|
|
||||||
|
|
||||||
void _onDragStart(ScaleStartDetails details) {
|
void _onDragStart(ScaleStartDetails details) {
|
||||||
_dragging = true;
|
_dragging = true;
|
||||||
|
|
||||||
@@ -343,25 +341,26 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
|
|
||||||
Widget _itemBuilder(BuildContext context, int index) {
|
Widget _itemBuilder(BuildContext context, int index) {
|
||||||
final item = widget.sources[index];
|
final item = widget.sources[index];
|
||||||
return Hero(
|
Widget child;
|
||||||
tag: item.url,
|
switch (item.sourceType) {
|
||||||
child: switch (item.sourceType) {
|
case SourceType.fileImage:
|
||||||
.fileImage => Image.file(
|
child = Image.file(
|
||||||
key: _keys[index],
|
key: _keys[index],
|
||||||
File(item.url),
|
File(item.url),
|
||||||
filterQuality: .low,
|
filterQuality: .low,
|
||||||
minScale: widget.minScale,
|
minScale: widget.minScale,
|
||||||
maxScale: widget.maxScale,
|
maxScale: widget.maxScale,
|
||||||
containerSize: _containerSize,
|
containerSize: _containerSize,
|
||||||
isAnimating: isAnimating,
|
|
||||||
onDragStart: _onDragStart,
|
onDragStart: _onDragStart,
|
||||||
onDragUpdate: _onDragUpdate,
|
onDragUpdate: _onDragUpdate,
|
||||||
onDragEnd: _onDragEnd,
|
onDragEnd: _onDragEnd,
|
||||||
tapGestureRecognizer: _tapGestureRecognizer,
|
tapGestureRecognizer: _tapGestureRecognizer,
|
||||||
horizontalDragGestureRecognizer: _horizontalDragGestureRecognizer,
|
horizontalDragGestureRecognizer: _horizontalDragGestureRecognizer,
|
||||||
onChangePage: _onChangePage,
|
onChangePage: _onChangePage,
|
||||||
),
|
);
|
||||||
.networkImage => Image(
|
case SourceType.networkImage:
|
||||||
|
final isLongPic = item.isLongPic;
|
||||||
|
child = Image(
|
||||||
key: _keys[index],
|
key: _keys[index],
|
||||||
image: CachedNetworkImageProvider(_getActualUrl(item.url)),
|
image: CachedNetworkImageProvider(_getActualUrl(item.url)),
|
||||||
minScale: widget.minScale,
|
minScale: widget.minScale,
|
||||||
@@ -385,7 +384,6 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
minScale: widget.minScale,
|
minScale: widget.minScale,
|
||||||
maxScale: widget.maxScale,
|
maxScale: widget.maxScale,
|
||||||
containerSize: _containerSize,
|
containerSize: _containerSize,
|
||||||
isAnimating: isAnimating,
|
|
||||||
onDragStart: null,
|
onDragStart: null,
|
||||||
onDragUpdate: null,
|
onDragUpdate: null,
|
||||||
onDragEnd: null,
|
onDragEnd: null,
|
||||||
@@ -408,12 +406,15 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
return child;
|
return child;
|
||||||
},
|
},
|
||||||
loadingBuilder: loadingBuilder,
|
loadingBuilder: loadingBuilder,
|
||||||
isAnimating: isAnimating,
|
|
||||||
onDragStart: _onDragStart,
|
onDragStart: _onDragStart,
|
||||||
onDragUpdate: _onDragUpdate,
|
onDragUpdate: _onDragUpdate,
|
||||||
onDragEnd: _onDragEnd,
|
onDragEnd: _onDragEnd,
|
||||||
),
|
);
|
||||||
.livePhoto => Obx(
|
if (isLongPic) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
case SourceType.livePhoto:
|
||||||
|
child = Obx(
|
||||||
key: _keys[index],
|
key: _keys[index],
|
||||||
() => _currIndex.value == index
|
() => _currIndex.value == index
|
||||||
? Viewer(
|
? Viewer(
|
||||||
@@ -421,7 +422,6 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
maxScale: widget.maxScale,
|
maxScale: widget.maxScale,
|
||||||
containerSize: _containerSize,
|
containerSize: _containerSize,
|
||||||
childSize: _containerSize,
|
childSize: _containerSize,
|
||||||
isAnimating: isAnimating,
|
|
||||||
onDragStart: _onDragStart,
|
onDragStart: _onDragStart,
|
||||||
onDragUpdate: _onDragUpdate,
|
onDragUpdate: _onDragUpdate,
|
||||||
onDragEnd: _onDragEnd,
|
onDragEnd: _onDragEnd,
|
||||||
@@ -437,9 +437,9 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
),
|
);
|
||||||
},
|
}
|
||||||
);
|
return Hero(tag: item.url, child: child);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onTap() {
|
void _onTap() {
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:io' show File;
|
import 'dart:io' show File;
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart';
|
import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart';
|
||||||
import 'package:PiliPlus/common/widgets/gesture/image_tap_gesture_recognizer.dart';
|
import 'package:PiliPlus/common/widgets/gesture/image_tap_gesture_recognizer.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image_viewer/viewer.dart';
|
import 'package:PiliPlus/common/widgets/image_viewer/viewer.dart';
|
||||||
@@ -37,7 +39,6 @@ class Image extends StatefulWidget {
|
|||||||
required this.minScale,
|
required this.minScale,
|
||||||
required this.maxScale,
|
required this.maxScale,
|
||||||
required this.containerSize,
|
required this.containerSize,
|
||||||
required this.isAnimating,
|
|
||||||
required this.onDragStart,
|
required this.onDragStart,
|
||||||
required this.onDragUpdate,
|
required this.onDragUpdate,
|
||||||
required this.onDragEnd,
|
required this.onDragEnd,
|
||||||
@@ -76,7 +77,6 @@ class Image extends StatefulWidget {
|
|||||||
required this.minScale,
|
required this.minScale,
|
||||||
required this.maxScale,
|
required this.maxScale,
|
||||||
required this.containerSize,
|
required this.containerSize,
|
||||||
required this.isAnimating,
|
|
||||||
required this.onDragStart,
|
required this.onDragStart,
|
||||||
required this.onDragUpdate,
|
required this.onDragUpdate,
|
||||||
required this.onDragEnd,
|
required this.onDragEnd,
|
||||||
@@ -122,7 +122,6 @@ class Image extends StatefulWidget {
|
|||||||
required this.minScale,
|
required this.minScale,
|
||||||
required this.maxScale,
|
required this.maxScale,
|
||||||
required this.containerSize,
|
required this.containerSize,
|
||||||
required this.isAnimating,
|
|
||||||
required this.onDragStart,
|
required this.onDragStart,
|
||||||
required this.onDragUpdate,
|
required this.onDragUpdate,
|
||||||
required this.onDragEnd,
|
required this.onDragEnd,
|
||||||
@@ -171,7 +170,6 @@ class Image extends StatefulWidget {
|
|||||||
required this.minScale,
|
required this.minScale,
|
||||||
required this.maxScale,
|
required this.maxScale,
|
||||||
required this.containerSize,
|
required this.containerSize,
|
||||||
required this.isAnimating,
|
|
||||||
required this.onDragStart,
|
required this.onDragStart,
|
||||||
required this.onDragUpdate,
|
required this.onDragUpdate,
|
||||||
required this.onDragEnd,
|
required this.onDragEnd,
|
||||||
@@ -220,7 +218,6 @@ class Image extends StatefulWidget {
|
|||||||
required this.minScale,
|
required this.minScale,
|
||||||
required this.maxScale,
|
required this.maxScale,
|
||||||
required this.containerSize,
|
required this.containerSize,
|
||||||
required this.isAnimating,
|
|
||||||
required this.onDragStart,
|
required this.onDragStart,
|
||||||
required this.onDragUpdate,
|
required this.onDragUpdate,
|
||||||
required this.onDragEnd,
|
required this.onDragEnd,
|
||||||
@@ -278,7 +275,6 @@ class Image extends StatefulWidget {
|
|||||||
final double maxScale;
|
final double maxScale;
|
||||||
final Size containerSize;
|
final Size containerSize;
|
||||||
|
|
||||||
final ValueGetter<bool> isAnimating;
|
|
||||||
final ValueChanged<ScaleStartDetails>? onDragStart;
|
final ValueChanged<ScaleStartDetails>? onDragStart;
|
||||||
final ValueChanged<ScaleUpdateDetails>? onDragUpdate;
|
final ValueChanged<ScaleUpdateDetails>? onDragUpdate;
|
||||||
final ValueChanged<ScaleEndDetails>? onDragEnd;
|
final ValueChanged<ScaleEndDetails>? onDragEnd;
|
||||||
@@ -566,27 +562,27 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
|
|||||||
_isListeningToStream = false;
|
_isListeningToStream = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _debugBuildErrorWidget(BuildContext context, Object error) {
|
// Widget _debugBuildErrorWidget(BuildContext context, Object error) {
|
||||||
return Stack(
|
// return Stack(
|
||||||
alignment: Alignment.center,
|
// alignment: Alignment.center,
|
||||||
children: <Widget>[
|
// children: <Widget>[
|
||||||
const Positioned.fill(child: Placeholder(color: Color(0xCF8D021F))),
|
// const Positioned.fill(child: Placeholder(color: Color(0xCF8D021F))),
|
||||||
Padding(
|
// Padding(
|
||||||
padding: const EdgeInsets.all(4.0),
|
// padding: const EdgeInsets.all(4.0),
|
||||||
child: FittedBox(
|
// child: FittedBox(
|
||||||
child: Text(
|
// child: Text(
|
||||||
'$error',
|
// '$error',
|
||||||
textAlign: TextAlign.center,
|
// textAlign: TextAlign.center,
|
||||||
textDirection: TextDirection.ltr,
|
// textDirection: TextDirection.ltr,
|
||||||
style: const TextStyle(
|
// style: const TextStyle(
|
||||||
shadows: <Shadow>[Shadow(blurRadius: 1.0)],
|
// shadows: <Shadow>[Shadow(blurRadius: 1.0)],
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
],
|
// ],
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -594,25 +590,31 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
|
|||||||
if (widget.errorBuilder != null) {
|
if (widget.errorBuilder != null) {
|
||||||
return widget.errorBuilder!(context, _lastException!, _lastStack);
|
return widget.errorBuilder!(context, _lastException!, _lastStack);
|
||||||
}
|
}
|
||||||
if (kDebugMode) {
|
// if (kDebugMode) {
|
||||||
return _debugBuildErrorWidget(context, _lastException!);
|
// return _debugBuildErrorWidget(context, _lastException!);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget result;
|
Widget result;
|
||||||
if (_imageInfo != null) {
|
if (_imageInfo != null) {
|
||||||
// final isLongPic =
|
double? minScale, maxScale;
|
||||||
// _imageInfo!.image.height / _imageInfo!.image.width >
|
final imgWidth = _imageInfo!.image.width.toDouble();
|
||||||
// StyleString.imgMaxRatio;
|
final imgHeight = _imageInfo!.image.height.toDouble();
|
||||||
|
final imgRatio = imgHeight / imgWidth;
|
||||||
|
final isLongPic =
|
||||||
|
imgRatio > StyleString.imgMaxRatio &&
|
||||||
|
imgHeight > widget.containerSize.height;
|
||||||
|
if (isLongPic) {
|
||||||
|
minScale =
|
||||||
|
widget.containerSize.width / widget.containerSize.height * imgRatio;
|
||||||
|
maxScale = math.max(widget.maxScale, minScale * 3);
|
||||||
|
}
|
||||||
result = Viewer(
|
result = Viewer(
|
||||||
minScale: widget.minScale,
|
minScale: minScale ?? widget.minScale,
|
||||||
maxScale: widget.maxScale,
|
maxScale: maxScale ?? widget.maxScale,
|
||||||
|
isLongPic: isLongPic,
|
||||||
containerSize: widget.containerSize,
|
containerSize: widget.containerSize,
|
||||||
childSize: Size(
|
childSize: Size(imgWidth, imgHeight),
|
||||||
_imageInfo!.image.width.toDouble(),
|
|
||||||
_imageInfo!.image.height.toDouble(),
|
|
||||||
),
|
|
||||||
isAnimating: widget.isAnimating,
|
|
||||||
onDragStart: widget.onDragStart,
|
onDragStart: widget.onDragStart,
|
||||||
onDragUpdate: widget.onDragUpdate,
|
onDragUpdate: widget.onDragUpdate,
|
||||||
onDragEnd: widget.onDragEnd,
|
onDragEnd: widget.onDragEnd,
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart';
|
import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart';
|
||||||
import 'package:PiliPlus/common/widgets/gesture/image_tap_gesture_recognizer.dart';
|
import 'package:PiliPlus/common/widgets/gesture/image_tap_gesture_recognizer.dart';
|
||||||
|
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
@@ -39,9 +39,9 @@ class Viewer extends StatefulWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.minScale,
|
required this.minScale,
|
||||||
required this.maxScale,
|
required this.maxScale,
|
||||||
|
this.isLongPic = false,
|
||||||
required this.containerSize,
|
required this.containerSize,
|
||||||
required this.childSize,
|
required this.childSize,
|
||||||
required this.isAnimating,
|
|
||||||
required this.onDragStart,
|
required this.onDragStart,
|
||||||
required this.onDragUpdate,
|
required this.onDragUpdate,
|
||||||
required this.onDragEnd,
|
required this.onDragEnd,
|
||||||
@@ -53,11 +53,11 @@ class Viewer extends StatefulWidget {
|
|||||||
|
|
||||||
final double minScale;
|
final double minScale;
|
||||||
final double maxScale;
|
final double maxScale;
|
||||||
|
final bool isLongPic;
|
||||||
final Size containerSize;
|
final Size containerSize;
|
||||||
final Size childSize;
|
final Size childSize;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
final ValueGetter<bool> isAnimating;
|
|
||||||
final ValueChanged<ScaleStartDetails>? onDragStart;
|
final ValueChanged<ScaleStartDetails>? onDragStart;
|
||||||
final ValueChanged<ScaleUpdateDetails>? onDragUpdate;
|
final ValueChanged<ScaleUpdateDetails>? onDragUpdate;
|
||||||
final ValueChanged<ScaleEndDetails>? onDragEnd;
|
final ValueChanged<ScaleEndDetails>? onDragEnd;
|
||||||
@@ -76,6 +76,7 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
|
|
||||||
_GestureType? _gestureType;
|
_GestureType? _gestureType;
|
||||||
|
|
||||||
|
Offset? _scalePos;
|
||||||
late double _scale;
|
late double _scale;
|
||||||
double? _scaleStart;
|
double? _scaleStart;
|
||||||
late Offset _position;
|
late Offset _position;
|
||||||
@@ -107,7 +108,7 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _reset() {
|
void _reset() {
|
||||||
_scale = 1.0;
|
_scale = widget.minScale;
|
||||||
_position = .zero;
|
_position = .zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,21 +119,15 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
widget.childSize,
|
widget.childSize,
|
||||||
widget.containerSize,
|
widget.containerSize,
|
||||||
).destination;
|
).destination;
|
||||||
// if (_imageSize.height / _imageSize.width > StyleString.imgMaxRatio) {
|
if (widget.isLongPic) {
|
||||||
// _imageSize = applyBoxFit(
|
final containerWidth = widget.containerSize.width;
|
||||||
// .fitWidth,
|
final containerHeight = widget.containerSize.height;
|
||||||
// widget.childSize,
|
final imageHeight = _imageSize.height * _scale;
|
||||||
// widget.containerSize,
|
_position = Offset(
|
||||||
// ).destination;
|
(1 - _scale) * containerWidth / 2,
|
||||||
// final containerWidth = widget.containerSize.width;
|
(imageHeight - _scale * containerHeight) / 2,
|
||||||
// final containerHeight = widget.containerSize.height;
|
);
|
||||||
// _scale = containerWidth / _imageSize.width;
|
}
|
||||||
// final imageHeight = _imageSize.height * _scale;
|
|
||||||
// _position = Offset(
|
|
||||||
// (1 - _scale) * containerWidth / 2,
|
|
||||||
// (imageHeight - _scale * containerHeight) / 2,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -223,32 +218,27 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleDoubleTap() {
|
void _handleDoubleTap() {
|
||||||
final Matrix4 begin;
|
final begin = Matrix4.identity()
|
||||||
final Matrix4 end;
|
..translateByDouble(_position.dx, _position.dy, 0.0, 1.0)
|
||||||
if (_scale == 1.0) {
|
..scaleByDouble(_scale, _scale, _scale, 1.0);
|
||||||
final imageWidth = _imageSize.width;
|
|
||||||
final imageHeight = _imageSize.height;
|
double endScale;
|
||||||
final isLongPic = imageHeight / imageWidth >= StyleString.imgMaxRatio;
|
if (_scale == widget.minScale) {
|
||||||
double scale = widget.maxScale * 0.6;
|
endScale = widget.maxScale * 0.6;
|
||||||
if (isLongPic) {
|
if (endScale <= widget.minScale) {
|
||||||
scale = widget.containerSize.width / _imageSize.width;
|
endScale = widget.maxScale;
|
||||||
} else {
|
|
||||||
scale = widget.maxScale * 0.6;
|
|
||||||
}
|
}
|
||||||
if (scale <= widget.minScale) {
|
|
||||||
scale = widget.maxScale;
|
|
||||||
}
|
|
||||||
begin = Matrix4.identity();
|
|
||||||
final position = _clampPosition(_downPos! * (1 - scale), scale);
|
|
||||||
end = Matrix4.identity()
|
|
||||||
..translateByDouble(position.dx, position.dy, 0.0, 1.0)
|
|
||||||
..scaleByDouble(scale, scale, scale, 1.0);
|
|
||||||
} else {
|
} else {
|
||||||
begin = Matrix4.identity()
|
endScale = widget.minScale;
|
||||||
..translateByDouble(_position.dx, _position.dy, 0.0, 1.0)
|
|
||||||
..scaleByDouble(_scale, _scale, _scale, 1.0);
|
|
||||||
end = Matrix4.identity();
|
|
||||||
}
|
}
|
||||||
|
final position = _clampPosition(
|
||||||
|
(_downPos! * (_scale - endScale) + _position * endScale) / _scale,
|
||||||
|
endScale,
|
||||||
|
);
|
||||||
|
final end = Matrix4.identity()
|
||||||
|
..translateByDouble(position.dx, position.dy, 0.0, 1.0)
|
||||||
|
..scaleByDouble(endScale, endScale, endScale, 1.0);
|
||||||
|
|
||||||
_tween
|
_tween
|
||||||
..begin = begin
|
..begin = begin
|
||||||
..end = end;
|
..end = end;
|
||||||
@@ -258,9 +248,25 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onScaleStart(ScaleStartDetails details) {
|
void _onScaleStart(ScaleStartDetails details) {
|
||||||
if (widget.isAnimating() || (details.pointerCount < 2 && _scale == 1.0)) {
|
if (details.pointerCount == 1) {
|
||||||
widget.onDragStart?.call(details);
|
if (widget.isLongPic) {
|
||||||
return;
|
final imageHeight = _scale * _imageSize.height;
|
||||||
|
final containerHeight = widget.containerSize.height;
|
||||||
|
if (_scalePos != null &&
|
||||||
|
(_round(_position.dy) ==
|
||||||
|
_round((imageHeight - _scale * containerHeight) / 2) &&
|
||||||
|
details.focalPoint.dy > _scalePos!.dy) ||
|
||||||
|
(_round(_position.dy) == _round(containerHeight - imageHeight) &&
|
||||||
|
details.focalPoint.dy < _scalePos!.dy)) {
|
||||||
|
_gestureType = .drag;
|
||||||
|
widget.onDragStart?.call(details);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (_scale == widget.minScale) {
|
||||||
|
_gestureType = .drag;
|
||||||
|
widget.onDragStart?.call(details);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_scaleStart = _scale;
|
_scaleStart = _scale;
|
||||||
@@ -268,7 +274,7 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onScaleUpdate(ScaleUpdateDetails details) {
|
void _onScaleUpdate(ScaleUpdateDetails details) {
|
||||||
if (widget.isAnimating() || (details.pointerCount < 2 && _scale == 1.0)) {
|
if (_gestureType == .drag) {
|
||||||
widget.onDragUpdate?.call(details);
|
widget.onDragUpdate?.call(details);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -298,11 +304,6 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
|
|
||||||
/// ref [InteractiveViewer]
|
/// ref [InteractiveViewer]
|
||||||
void _onScaleEnd(ScaleEndDetails details) {
|
void _onScaleEnd(ScaleEndDetails details) {
|
||||||
if (widget.isAnimating() || (details.pointerCount < 2 && _scale == 1.0)) {
|
|
||||||
widget.onDragEnd?.call(details);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (_gestureType) {
|
switch (_gestureType) {
|
||||||
case _GestureType.pan:
|
case _GestureType.pan:
|
||||||
if (details.velocity.pixelsPerSecond.distance < kMinFlingVelocity) {
|
if (details.velocity.pixelsPerSecond.distance < kMinFlingVelocity) {
|
||||||
@@ -337,31 +338,35 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
..duration = Duration(milliseconds: (tFinal * 1000).round())
|
..duration = Duration(milliseconds: (tFinal * 1000).round())
|
||||||
..forward(from: 0);
|
..forward(from: 0);
|
||||||
case _GestureType.scale:
|
case _GestureType.scale:
|
||||||
// if (details.scaleVelocity.abs() < 0.1) {
|
// if (details.scaleVelocity.abs() < 0.1) {
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// final double scale = _scale;
|
// final double scale = _scale;
|
||||||
// final FrictionSimulation frictionSimulation = FrictionSimulation(
|
// final FrictionSimulation frictionSimulation = FrictionSimulation(
|
||||||
// _interactionEndFrictionCoefficient * _scaleFactor,
|
// _interactionEndFrictionCoefficient * _scaleFactor,
|
||||||
// scale,
|
// scale,
|
||||||
// details.scaleVelocity / 10,
|
// details.scaleVelocity / 10,
|
||||||
// );
|
// );
|
||||||
// final double tFinal = _getFinalTime(
|
// final double tFinal = _getFinalTime(
|
||||||
// details.scaleVelocity.abs(),
|
// details.scaleVelocity.abs(),
|
||||||
// _interactionEndFrictionCoefficient,
|
// _interactionEndFrictionCoefficient,
|
||||||
// effectivelyMotionless: 0.1,
|
// effectivelyMotionless: 0.1,
|
||||||
// );
|
// );
|
||||||
// _scaleAnimation = _scaleController.drive(
|
// _scaleAnimation = _scaleController.drive(
|
||||||
// Tween<double>(
|
// Tween<double>(
|
||||||
// begin: scale,
|
// begin: scale,
|
||||||
// end: frictionSimulation.x(tFinal),
|
// end: frictionSimulation.x(tFinal),
|
||||||
// ).chain(CurveTween(curve: Curves.decelerate)),
|
// ).chain(CurveTween(curve: Curves.decelerate)),
|
||||||
// )..addListener(_handleScaleAnimation);
|
// )..addListener(_handleScaleAnimation);
|
||||||
// _effectiveAnimationController
|
// _effectiveAnimationController
|
||||||
// ..duration = Duration(milliseconds: (tFinal * 1000).round())
|
// ..duration = Duration(milliseconds: (tFinal * 1000).round())
|
||||||
// ..forward(from: 0);
|
// ..forward(from: 0);
|
||||||
|
break;
|
||||||
|
case _GestureType.drag:
|
||||||
|
widget.onDragEnd?.call(details);
|
||||||
case null:
|
case null:
|
||||||
}
|
}
|
||||||
|
_scalePos = null;
|
||||||
_gestureType = null;
|
_gestureType = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,6 +390,7 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onPointerDown(PointerDownEvent event) {
|
void _onPointerDown(PointerDownEvent event) {
|
||||||
|
_scalePos = event.position;
|
||||||
_tapGestureRecognizer.addPointer(event);
|
_tapGestureRecognizer.addPointer(event);
|
||||||
_doubleTapGestureRecognizer.addPointer(event);
|
_doubleTapGestureRecognizer.addPointer(event);
|
||||||
_horizontalDragGestureRecognizer
|
_horizontalDragGestureRecognizer
|
||||||
@@ -440,7 +446,9 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum _GestureType { pan, scale }
|
double _round(double value) => value.toPrecision(6);
|
||||||
|
|
||||||
|
enum _GestureType { pan, scale, drag }
|
||||||
|
|
||||||
double _getFinalTime(
|
double _getFinalTime(
|
||||||
double velocity,
|
double velocity,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
|
|
||||||
enum SourceType { fileImage, networkImage, livePhoto }
|
enum SourceType { fileImage, networkImage, livePhoto }
|
||||||
|
|
||||||
class SourceModel {
|
class SourceModel {
|
||||||
@@ -8,6 +6,7 @@ class SourceModel {
|
|||||||
final String? liveUrl;
|
final String? liveUrl;
|
||||||
final int? width;
|
final int? width;
|
||||||
final int? height;
|
final int? height;
|
||||||
|
final bool isLongPic;
|
||||||
|
|
||||||
const SourceModel({
|
const SourceModel({
|
||||||
this.sourceType = SourceType.networkImage,
|
this.sourceType = SourceType.networkImage,
|
||||||
@@ -15,12 +14,6 @@ class SourceModel {
|
|||||||
this.liveUrl,
|
this.liveUrl,
|
||||||
this.width,
|
this.width,
|
||||||
this.height,
|
this.height,
|
||||||
|
this.isLongPic = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
bool get isLongPic {
|
|
||||||
if (width != null && height != null) {
|
|
||||||
return height! / width! > StyleString.imgMaxRatio;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user