【问题标题】:Flutter zoomable image with overlays带有叠加层的颤振可缩放图像
【发布时间】:2019-10-23 06:09:41
【问题描述】:

我正在尝试制作一个自定义小部件,用户可以缩放和平移图像并点击以覆盖图像上的点。我想我已经接近了,但不能完全得到点在水龙头下排队。

我从这里复制了相关的缩放平移: https://github.com/flutter/flutter/blob/master/examples/layers/widgets/gestures.dart

我还根据这篇文章将我的 GestureDetector 拉出到它自己的小部件中,以获得正确的 RenderBox 以进行全局到本地偏移转换 flutter : Get Local position of Gesture Detector

import 'package:flutter/material.dart';

class ImageWithOverlays extends StatefulWidget {
  final List<FractionalOffset> fractionalOffsets;
  ImageWithOverlays(this.fractionalOffsets);

  @override
  _ImageWithOverlaysState createState() => _ImageWithOverlaysState();    
}


class _ImageWithOverlaysState extends State<ImageWithOverlays> {
  Offset _startingFocalPoint;
  Offset _previousOffset;
  Offset _offset = Offset.zero;

  double _previousZoom;
  double _zoom = 1.0;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        onScaleStart: _handleScaleStart,
        onScaleUpdate:_handleScaleUpdate,
        onTapDown: (details){          
          setState(() {                        
            widget.fractionalOffsets.add(_getFractionalOffset(context, details.globalPosition));             
          });
        },
        onDoubleTap: _handleScaleReset,
        child: Transform(
        transform: Matrix4.diagonal3Values(_zoom, _zoom, 1.0) + Matrix4.translationValues(_offset.dx, _offset.dy, 0.0),         
        child: Center(
          child: Container(
              child:Stack(
              children: <Widget>[
                Image.network(
                  'https://picsum.photos/250?image=9',
                ), ]
                ..addAll(_getOverlays(context)),
            )),
          ),
      ));
     
  }

  void _handleScaleStart(ScaleStartDetails details) {
    setState(() {
      _startingFocalPoint = details.focalPoint;
      _previousOffset = _offset;
      _previousZoom = _zoom;
    });
  }


  void _handleScaleUpdate(ScaleUpdateDetails details) {
    setState(() {
      _zoom = _previousZoom * details.scale;

      // Ensure that item under the focal point stays in the same place despite zooming
      final Offset normalizedOffset = (_startingFocalPoint - _previousOffset) / _previousZoom;
      _offset = details.focalPoint - normalizedOffset * _zoom;
    });
  }

  void _handleScaleReset() {
    setState(() {
      _zoom = 1.0;
      _offset = Offset.zero;
      widget.fractionalOffsets.clear();
    });
  }

  List<Widget> _getOverlays(BuildContext context) {
    return widget.fractionalOffsets
        .asMap() 
        .map((i, fo) => MapEntry(
            i,
            Align(
                alignment: fo,
                child: _buildIcon((i + 1).toString(), context)
            )           
        ))
        .values
        .toList();
  }

  Widget _buildIcon(String indexText, BuildContext context) {
    return FlatButton.icon(      
      icon: Icon(Icons.location_on, color: Colors.red),
      label: Text(
        indexText,
        style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
      ),      
    );
  }

  Widget _buildCircleIcon(String indexText) {
    return Container(          
          margin: const EdgeInsets.all(8.0),
          padding: const EdgeInsets.all(8.0),
          decoration: BoxDecoration(
            color: Colors.red,
            shape: BoxShape.circle,
          ),
          child:  Text(
                indexText,
                style: TextStyle(
                  fontSize: 16,
                  color: Colors.white,
                  fontWeight: FontWeight.bold
                ),
              ));
  }

  FractionalOffset _getFractionalOffset(BuildContext context, Offset globalPosition) {    
    var renderbox = context.findRenderObject() as RenderBox;
    var localOffset = renderbox.globalToLocal(globalPosition);
    var width = renderbox.size.width;
    var height = renderbox.size.height;
    return FractionalOffset(localOffset.dx/width,localOffset.dy/height); 
  }
}

但我很难让布局正确。我认为我有两个问题:

  1. 图像喜欢跳到左上角。我不知道如何让它保持居中。
  2. 当我放大时,我的 _getFractionalOffset 函数没有返回正确的分数。此屏幕截图中的图标未在我的点击下显示 我的矩阵数学很差,我对 Flutter 如何进行转换的理解也很差,所以我将不胜感激。

【问题讨论】:

标签: image flutter dart flutter-layout overlay


【解决方案1】:

想通了,我的代码有几个问题,当复合时,调试很烦人。

首先,我应该将比例/平移矩阵相乘,而不是相加:

vector.Matrix4 get _transformationMatrix {
  var scale = vector.Matrix4.diagonal3Values(_zoom, _zoom, 1.0);
  var translation = vector.Matrix4.translationValues(_offset.dx, _offset.dy, 0.0);
  var transform = translation * scale;
  return transform;
}

其次,我试图将本地偏移量转换为图像帧而不是让 vector_math 为我做这件事。新的叠加偏移量计算:

Offset _getOffset(BuildContext context, Offset globalPosition) {    
  var renderbox = context.findRenderObject() as RenderBox;
  var localOffset = renderbox.globalToLocal(globalPosition);    
  var localVector = vector.Vector3(localOffset.dx, localOffset.dy,0);
  var transformed =  Matrix4.inverted(_transformationMatrix).transform3(localVector);
  return Offset(transformed.x, transformed.y);
}

由于_transformationmatrix 将一个点从图像帧到屏幕帧,我需要左乘逆以从屏幕帧 (localOffset) 到图像帧(返回值)。

最后,我使用的是 Positioned 小部件而不是 Align,这样我就可以以像素为单位设置定位:

double _width = 100;
double _height = 50;
List<Widget> _getOverlays(BuildContext context) {
  return widget.points
      .asMap() 
      .map((i, offset) => MapEntry(
          i,
          Positioned(
              left: offset.dx-_width/2,
              width:_width,
              top: offset.dy -_height/2,
              height:_height,
              child: _buildIcon((i + 1).toString(), context)
          )           
      ))
      .values
      .toList();
}

其中widget.points 是上面_getOffset 返回的偏移量的List&lt;Offset&gt;width/height 是我的图标小部件的宽度和高度。

希望这对某人有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-10
    • 2021-10-07
    • 2019-09-21
    • 1970-01-01
    相关资源
    最近更新 更多