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


    前言

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

    手势检测

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

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

    class PaintedBoardProvider extends ChangeNotifier {
      // 偏移量
      double translationX = 0;
      double translationY = 0;
    
    • 1
    • 2
    • 3
    • 4

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

    class _HandPaintedBoardState extends State {
      // 记录缩放开始的坐标
      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);
            }
          },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    由于我们的画布偏移是跟缩放共用 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;
            }
          },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    画布移动

    在上文「手绘板的制作——画布缩放(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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    最主要的参数为 transform:

      final Matrix4 transform;
    
    • 1

    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,
            ),
          ),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    这里有一点要注意,之前我们的缩放流程是基于中心点进行缩放的,所以 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();
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

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

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

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

  • 相关阅读:
    数电课程设计——课设一:加减计数器
    如何避免预读失效和缓存污染的问题?
    控价为什么一定要先监测价格
    LeetCode刷题——有序矩阵中第 K 小的元素#378#Medium
    【校招VIP】去年招770人,今年竟然只招50人??24届秋招真的好难。。。
    【779. 第K个语法符号】
    二蛋赠书五期:《Python数据挖掘:入门、进阶与实用案例分析》
    Unity下如何实现RTMP或RTSP播放端录像?
    数据可视化 复习笔记2022
    毫米波点云雷达 论文阅读 | 3DRIMR, IPCCC 2021
  • 原文地址:https://blog.csdn.net/m0_46278918/article/details/125279016