【问题标题】:Keep a widget at a relative point even when image get's scaled即使图像被缩放,也将小部件保持在相对点
【发布时间】:2020-01-02 20:37:08
【问题描述】:

几天以来,我试图在图像缩放(放大/缩小)时在图像上保留一个绘制的圆圈(使用CustomPainter)。要渲染图像并启用缩放手势,我使用photo_view。只要没有缩放,我就可以根据视口的变化移动圆圈。所有值(比例、偏移)都由 PhotoViewController 作为流提供,如下所示:

ma​​in.dart

import 'package:backtrack/markerPainter.dart';
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
import 'dart:math' as math;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  Offset position;
  Offset startPosition;
  Offset scaleChange;
  Offset offsetToCenter;
  double initialScale;
  Offset forPoint;
  PhotoViewScaleStateController controller;
  PhotoViewController controller2;
  double lastscale;
  @override
  void initState() {
    super.initState();
    controller = PhotoViewScaleStateController();
    controller2 = PhotoViewController();
    controller2.outputStateStream.listen((onData) {
     // print("position change: " + onData.position.toString());
     // print("scale change: " + onData.scale.toString());
      if (position != null) {
        setState(() {
          final scaleToUser = initialScale + (initialScale - onData.scale);
          //Change to the unscaled point inverted to reflect the opposite movement of the user
          final newOffset = (offsetToCenter - onData.position) * -1;
          print("new offset: " + newOffset.toString());

          if (onData.scale != lastscale) {
            if (onData.scale > initialScale) {
              //zoom in
              var zoomeChnage = initialScale-onData.scale;
              scaleChange = startPosition * zoomeChnage;
              print("new scaleChange: " + scaleChange.toString());
            } else {
              //zoom out
            }
            lastscale = onData.scale;
          }
          forPoint = newOffset-scaleChange; 
          print("new forPoint: " + forPoint.toString());
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: _buildMap(context));
  }

  Widget _buildMap(BuildContext context) {
    return Container(
      child: CustomPaint(
        foregroundPainter: MyPainter(position, forPoint),
        child: PhotoView(
          imageProvider: AssetImage("asset/map.JPG"),
          onTapUp: (a, b, c) {
            setState(() {
              position = b.localPosition;
              offsetToCenter = c.position;
              forPoint = Offset.zero;
              initialScale = c.scale;
              print("localPosition: " + b.localPosition.toString());
              print("offsetToCenter: " + offsetToCenter.toString());
              print("initialScale: " + initialScale.toString());
            });
          },
          scaleStateController: controller,
          controller: controller2,
        ),
      ),
    );
  }
}

以防万一,下面是在图像顶部呈现圆圈的小部件。 ma​​rkerPainter.dart

class MyPainter extends CustomPainter {
  final Offset position;
  final Offset centerOffset;
  final Paint line;
  MyPainter(this.position, this.centerOffset)
      : line = new Paint()
          ..color = Colors.pink
          ..style = PaintingStyle.fill
          ..strokeWidth = 5;

  @override
  void paint(Canvas canvas, Size size) {
    if (position == null) {
      return;
    }

    canvas.drawCircle(position + centerOffset, 5, line);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

我确实尝试了很多,但似乎我的大脑无法解决它,非常欢迎任何帮助。

【问题讨论】:

  • 能否请您说明您的问题是什么以及您要达到的目标。
  • @JoãoSoares 我的问题是即使用户放大/缩小,也要将绘制的圆圈保持在同一位置。我的目标是允许用户在图像中标记点(例如在地图上放置标记)。
  • 我现在明白了。听起来确实很棘手。我会试一试,但我不能做出任何承诺。

标签: flutter drawing scale


【解决方案1】:

我找到了一个解决方案,使用这个帮助类我可以计算一个绝对点的相对点:

  FlatMapState(
      {this.imageScale,
      this.initialViewPort,
      this.imageSize,
      this.viewPortDelta,
      this.viewPortOffsetToCenter,
      this.currentViewPort});

  Offset absolutePostionToViewPort(Offset absolutePosition) {
    var relativeViewPortPosition =
        ((imageSize * imageScale).center(Offset.zero) -
                absolutePosition * imageScale) *
            -1;
    relativeViewPortPosition += viewPortOffsetToCenter;
    return relativeViewPortPosition + viewPortDelta / -2;
  }
}

该类或多或少地从 PhotoViewController 更新事件中获取所有信息,如下所示:

bool _photoViewValueIsValid(PhotoViewControllerValue value) {
    return value != null && value.position != null && value.scale != null;
  }

  FlatMapState createCurrentState(PhotoViewControllerValue photoViewValue) {
    if (_photoViewValueIsValid(photoViewValue) && imageSize != null) {
      if (lastViewPort == null) {
        lastViewPort = stickyKey.currentContext.size;
      }
      return FlatMapState(
        imageScale: photoViewValue.scale,
        imageSize: imageSize,
        initialViewPort: lastViewPort,
        viewPortDelta: Offset(
            lastViewPort.width - stickyKey.currentContext.size.width,
            lastViewPort.height - stickyKey.currentContext.size.height),
        viewPortOffsetToCenter: photoViewValue.position,
        currentViewPort: Offset(stickyKey.currentContext.size.width,
            stickyKey.currentContext.size.height),
      );
    } else {
      //The map or image is still loading, the state can't be generated
      return null;
    }
  }

请注意,必须通过将原点设置为中心来绘制计算值,如下所示:

 @override
  void paint(Canvas canvas, Size size) {
    //In case the state is null or nothing to draw leave
    if(flatMapState == null || drawableElements == null){
      return;
    }
    //Center the canvas (0,0) is now the center, done to have the same origion as photoview
    var viewPortCenter = flatMapState.initialViewPort.center(Offset.zero);
    canvas.translate(viewPortCenter.dx, viewPortCenter.dy);
    drawableElements.forEach((drawable) => drawable.draw(
          canvas,
          size,
          flatMapState,
        ));
  }

【讨论】:

    猜你喜欢
    • 2019-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-13
    相关资源
    最近更新 更多