我想在双击图像时启用放大和缩小,以及在捏合时放大/缩小。 我在 YouTube 上看到了一些教程,他们使用
GestureDetector
来实现此功能,例如 this one,但由于某种原因,它对我来说不起作用。
为了实现捏合缩放,我依赖于这个答案,它确实效果很好,但我也想在双击图像时启用放大/缩小。不幸的是,在互联网上查找了一种方法,没有得到任何结果。
有没有办法使用
InteractiveViewer
来通过捏合和双击来实现放大/缩小?
这是我的代码:
@override
Widget build(BuildContext context) {
return Center(
child: InteractiveViewer(
boundaryMargin: EdgeInsets.all(80),
panEnabled: false,
scaleEnabled: true,
minScale: 1.0,
maxScale: 2.2,
child: Image.network("https://pngimg.com/uploads/muffin/muffin_PNG123.png",
fit: BoxFit.fitWidth,
)
),
);
}
您可以使用
GestureDetector
,它为您提供单击的位置,并且您可以使用 TransformationController
在单击位置进行缩放:
final _transformationController = TransformationController();
TapDownDetails _doubleTapDetails;
@override
Widget build(BuildContext context) {
return GestureDetector(
onDoubleTapDown: (d) => _doubleTapDetails = d,
onDoubleTap: _handleDoubleTap,
child: Center(
child: InteractiveViewer(
transformationController: _transformationController,
/* ... */
),
),
);
}
void _handleDoubleTap() {
if (_transformationController.value != Matrix4.identity()) {
_transformationController.value = Matrix4.identity();
} else {
final position = _doubleTapDetails.localPosition;
// For a 3x zoom
_transformationController.value = Matrix4.identity()
..translate(-position.dx * 2, -position.dy * 2)
..scale(3.0);
// Fox a 2x zoom
// ..translate(-position.dx, -position.dy)
// ..scale(2.0);
}
}
要在双击时为过渡设置动画,您必须在 Till 的代码之上创建一个显式动画。
class _WidgetState extends State<Widget> with SingleTickerProviderStateMixin {
.
.
.
AnimationController _animationController;
Animation<Matrix4> _animation;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 400),
)..addListener(() {
_transformationController.value = _animation.value;
});
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
.
.
.
void _handleDoubleTap() {
Matrix4 _endMatrix;
Offset _position = _doubleTapDetails.localPosition;
if (_transformationController.value != Matrix4.identity()) {
_endMatrix = Matrix4.identity();
} else {
_endMatrix = Matrix4.identity()
..translate(-_position.dx * 2, -_position.dy * 2)
..scale(3.0);
}
_animation = Matrix4Tween(
begin: _transformationController.value,
end: _endMatrix,
).animate(
CurveTween(curve: Curves.easeOut).animate(_animationController),
);
_animationController.forward(from: 0);
}
.
.
.
}
这是一个完整的便携式解决方案,其中包含可定制的动画:
class DoubleTappableInteractiveViewer extends StatefulWidget {
final double scale;
final Duration scaleDuration;
final Curve curve;
final Widget child;
const DoubleTappableInteractiveViewer({
super.key,
this.scale = 2,
this.curve = Curves.fastLinearToSlowEaseIn,
required this.scaleDuration,
required this.child,
});
@override
State<DoubleTappableInteractiveViewer> createState() => _DoubleTappableInteractiveViewerState();
}
class _DoubleTappableInteractiveViewerState extends State<DoubleTappableInteractiveViewer>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
Animation<Matrix4>? _zoomAnimation;
late TransformationController _transformationController;
TapDownDetails? _doubleTapDetails;
@override
void initState() {
super.initState();
_transformationController = TransformationController();
_animationController = AnimationController(
vsync: this,
duration: widget.scaleDuration,
)..addListener(() {
_transformationController.value = _zoomAnimation!.value;
});
}
@override
void dispose() {
_transformationController.dispose();
_animationController.dispose();
super.dispose();
}
void _handleDoubleTapDown(TapDownDetails details) {
_doubleTapDetails = details;
}
void _handleDoubleTap() {
final newValue =
_transformationController.value.isIdentity() ?
_applyZoom() : _revertZoom();
_zoomAnimation = Matrix4Tween(
begin: _transformationController.value,
end: newValue,
).animate(
CurveTween(curve: widget.curve)
.animate(_animationController)
);
_animationController.forward(from: 0);
}
Matrix4 _applyZoom() {
final tapPosition = _doubleTapDetails!.localPosition;
final translationCorrection = widget.scale - 1;
final zoomed = Matrix4.identity()
..translate(
-tapPosition.dx * translationCorrection,
-tapPosition.dy * translationCorrection,
)
..scale(widget.scale);
return zoomed;
}
Matrix4 _revertZoom() => Matrix4.identity();
@override
Widget build(BuildContext context) {
return GestureDetector(
onDoubleTapDown: _handleDoubleTapDown,
onDoubleTap: _handleDoubleTap,
child: InteractiveViewer(
transformationController: _transformationController,
child: widget.child,
),
);
}
}
使用示例:
DoubleTappableInteractiveViewer(
scaleDuration: const Duration(milliseconds: 600),
child: Image.network(imageUrl),
),
在 dartpad 上玩转它。
void zoomDoubleTap(TapDownDetails details, pdfx.PdfControllerPinch controller) {
double currentZoom = controller.zoomRatio;
if (currentZoom <= 1.0) {
final Matrix4 newMatrix = Matrix4.copy(controller.value)
..translate(
-details.localPosition.dx,
-(details.localPosition.dy + controller.viewRect.top),
)
..scale(2.0);
controller.goTo(destination: newMatrix);
} else if (currentZoom <= 2.0) {
final Matrix4 newMatrix = Matrix4.copy(controller.value)
..translate(
-(details.localPosition.dx + controller.viewRect.left),
-(details.localPosition.dy + controller.viewRect.top),
)
..scale(3.0);
controller.goTo(destination: newMatrix);
} else {
final Matrix4 newMatrix = Matrix4.identity()..scale(1.0);
controller.goTo(destination: newMatrix);
}
}
允许您在此配置中进行自定义缩放的方法可以缩放 100% -> 200% -> 300%,然后返回 100%
用途:
import 'package:pdfx/pdfx.dart' as pdfx;
final pdfController = pdfx.PdfControllerPinch(document: pdfx.PdfDocument.openData(/*your document*/));
GestureDetector(
onDoubleTapDown: (details) {
zoomDoubleTap(details, pdfController);
},
child: pdfx.PdfViewPinch(
backgroundDecoration: const BoxDecoration(),
padding: 0,
builders: const
pdfx.PdfViewPinchBuilders<pdfx.DefaultBuilderOptions>(
options: pdfx.DefaultBuilderOptions(
loaderSwitchDuration:
Duration(milliseconds:800)),
),
controller: pdfController,
),
);