手绘板的制作——画布移动(5)

前言

在上文「手绘板的制作——画布缩放(4)」中,我们学会了画布的缩放,这节我们学习下画布的移动,毕竟放大的画布不能移动的话,那放大还有什么意义。=_=

手势检测

既然要移动,那当然需要检测手势,由于单指的移动我们已经处理成手绘了,所以,我们把双指的移动理解为真正的画布移动。

首先,我们需要两个值来保存 x、y 的偏移量:

class PaintedBoardProvider extends ChangeNotifier {
  // 偏移量
  double translationX = 0;
  double translationY = 0;

然后我们需要在 onScaleStart 中存储当前的焦点位置以及当前的偏移量:

class _HandPaintedBoardState extends State<HandPaintedBoard> {
  // 记录缩放开始的坐标
  double _startX = 0;    // <- 新增
  double _startY = 0;    // <- 新增
  // 记录缩放开始的偏移量
  double _startTranslationX = 0;    // <- 新增
  double _startTranslationY = 0;    // <- 新增

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onScaleStart: (details) {
        if (details.pointerCount > 1) {  // 双指
          _gestureType = GestureType.scale;
          _startScale = _paintedBoardProvider.scale;
          _startX = details.localFocalPoint.dx;    // <- 新增
          _startY = details.localFocalPoint.dy;    // <- 新增
          _startTranslationX = _paintedBoardProvider.translationX;    // <- 新增
          _startTranslationY = _paintedBoardProvider.translationY;    // <- 新增
        } else { // 单指
          _gestureType = GestureType.translate;
          _paintedBoardProvider.onStart(details.localFocalPoint);
        }
      },

由于我们的画布偏移是跟缩放共用 onScaleUpdate 的,所以,我们需要进行判断,当实际的缩放系数小于等于 0.1 的时候,我们理解为用户想要偏移,当实际的缩放系数大于 0.1 的时候,我们理解为用户想要缩放画布:

      onScaleUpdate: (details) {
        switch (_gestureType) {
          case GestureType.translate:
            _paintedBoardProvider.onUpdate(details.localFocalPoint);
            break;
          case GestureType.scale:
            if ((details.scale - 1).abs() > 0.1) {
              setState(() {
                _paintedBoardProvider.scale = _startScale + details.scale - 1;
              });
            } else {
              setState(() {
                _paintedBoardProvider.translationX = _startTranslationX +
                    details.localFocalPoint.dx - _startX;
                _paintedBoardProvider.translationY = _startTranslationY +
                    details.localFocalPoint.dy - _startY;
              });
            }
            break;
        }
      },

画布移动

在上文「手绘板的制作——画布缩放(4)」中,我们的缩放是通过 Transform.scale 来解决的,虽然也有 Transform.translate 来解决偏移的问题,但是由于我们是缩放和偏移都需要的,所以我们直接使用 Transform 来实现:

  const Transform({
    Key? key,
    required this.transform,
    this.origin,
    this.alignment,
    this.transformHitTests = true,
    this.filterQuality,
    Widget? child,
  }) : assert(transform != null),
       super(key: key, child: child);

最主要的参数为 transform:

  final Matrix4 transform;

Matrix4 是一个 4 * 4 的矩阵,用于控制缩放、移动、旋转什么的,具体这里就不细说,不是这节的重要内容,有需要后续再补一篇相关的内容,目前,我们只需要知道以下几点即可:

  • Matrix4 有 16 个参数
  • 从 1 开始算,第 1、6、11 参数的值分别标识 x 、y、z 轴的缩放,目前我们只需要 x、y 的缩放,所以只填 1、6 即可。
  • 从 1 开始算,第 13、14、15 参数的值分别标识 x 、y、z 轴的偏移,目前我们只需要 x、y 的偏移,所以只填 13、14 即可。

具体要填写的代码就是这样:

      child: Transform(
        alignment: Alignment.center,
        transform: Matrix4(
          _paintedBoardProvider.scale,
          0,
          0,
          0,
          0,
          _paintedBoardProvider.scale,
          0,
          0,
          0,
          0,
          1,
          0,
          _paintedBoardProvider.translationX,
          _paintedBoardProvider.translationY,
          0,
          1,
        ),
        child: CustomPaint(
          painter: MyPainter(_paintedBoardProvider),
          size: Size.infinite,
        ),
      ),

这里有一点要注意,之前我们的缩放流程是基于中心点进行缩放的,所以 alignment 填 Alignment.center

剩下就是差坐标点的偏移了:

  /// 移动开始时
  void onStart(Offset localPosition) {
    double startX = localPosition.dx;
    double startY = localPosition.dy;
    final newStroke = Stroke(
      color: isClear ? Colors.transparent : color,
      width: paintWidth,
      isClear: isClear,
    );
    newStroke.path.moveTo(  // <- 调整
        (startX + (scale - 1) * realCanvasSize.width / 2 - translationX) /
            scale,
        (startY + (scale - 1) * realCanvasSize.height / 2 - translationY) /
            scale);
    _strokes.add(newStroke);
  }

  /// 移动
  void onUpdate(Offset localPosition) {
    _strokes.last.path.lineTo( // <- 调整
        (localPosition.dx +
                (scale - 1) * realCanvasSize.width / 2 -
                translationX) /
            scale,
        (localPosition.dy +
                (scale - 1) * realCanvasSize.height / 2 -
                translationY) /
            scale);
    notifyListeners();
  }

注意,要在除以 scale 之前减去偏移量,为什么要除以 scale?这个上文已经讲过,这里就不重复说明了。

至此,移动功能就完成了。

若有需要,后续再把整个项目的代码贴下,今天先到这里了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值