Flutter:使用 InteractiveViewer 双击启用图像放大/缩小

问题描述 投票:0回答:4

我想在双击图像时启用放大和缩小,以及在捏合时放大/缩小。 我在 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,
      )
    ),
  );
}
android flutter dart zooming
4个回答
39
投票

您可以使用

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);
  }
}

14
投票

要在双击时为过渡设置动画,您必须在 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);
  }
  .
  .
  .
}

8
投票

这是一个完整的便携式解决方案,其中包含可定制的动画:

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 上玩转它。


0
投票
    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,
                 ),
               );
© www.soinside.com 2019 - 2024. All rights reserved.