Flutter 双指缩放和移动手势检测系列之--1方案

Flutter 双指缩放和移动共存手势检测

  • 前言
  • 关于Flutter缩放和移动
  • 最终效果
  • GestureDetector 移动和缩放动作
  • 双指缩放和移动Gesture方案
  • 源码解读
  • 方案小结
    • 小结
    • 局限
    • 代码
    • 示例项目体验
  • 下一篇

前言

本文所采用Flutter 版本为 3.10.6, 也许后续版本官方会对手势做进一步丰富完善,以解决本文涉及到的解决方案。

本文涉及项目手势需求:双指向外或内触屏伸缩,对图片进行缩放;双指在屏幕上同向触屏移动,移动图片。

关于Flutter缩放和移动

Flutter 使用中采用GestureDetector进行移动或是手势判定,该手势检测器内置了移动,缩放,长按等动作检测。如果应用在缩放和移动上是单手势,该控件能够满足应用需求。但是如果是个复合手势,如手势可能是缩放也可能是移动,然后就可能会有冲突。

本文就是基于实际项目中遇到的需要双指缩放和双指移动并存手势,在GestureDetector基础上构建一个双手指并存手势控件

最终效果

这是制作好后最终效果图:

应用商店搜索七彩涂色体验

GestureDetector 移动和缩放动作

  1. GestureDetector 移动和手势列表
动作 回调 冲突动作
垂直移动 onVerticalDrag* 水平移动
水平移动 onVerticalDrag* 垂直移动
移动 onPan* 缩放
缩放 onScale* 移动

如上表所示,缩放和移动动作是相互冲突。如果同时指定了缩放回调和移动回调,就会遇到如下图的异常。
Flutter 双指缩放和移动手势检测系列之--1方案_第1张图片

  1. GestureDetector 双指缩放和移动共存测试结果
组合 效果 结论
只移动 单双指均可行 单指头拖拽图片和手势跟随很好,但是双指拖拽移动增量超过双指移动
缩放 + 移动 直接异常 查看源码,无法共存,抛出异常
缩放 + 水平移动或垂直移动 不冲突,可移动 缩放或移动内部自行断定,效果很好;但是只能水平移动或垂直移动
缩放 + 水平移动+垂直移动 直接异常 查看源码,水平移动和垂直移动无法共存,抛出异常

从上述测试表中可以看到要实现本文的双指缩放和移动共存,采用GestureDetector 不可实现。

双指缩放和移动Gesture方案

  1. 方案思路
    通过触摸点来进行来进行判断,如果触摸点之间距离在增加并大于阀值,则是缩放。如果触摸点之间距离相同,但是与起始点距离超过阀值,则是移动。

缩放示意图
缩放开始时,触点的中心位置基本保持不变。中心点相对不变则,但是触点距离变大,则是缩放动作。
Flutter 双指缩放和移动手势检测系列之--1方案_第2张图片

移动示意图
移动开始时,触点之间相对距离不变,但是与起始点有较大位移。中心点位移,触摸点相对位置不变则是移动。

Flutter 双指缩放和移动手势检测系列之--1方案_第3张图片

手势流程

Flutter 双指缩放和移动手势检测系列之--1方案_第4张图片 2. 与**GestureDetector** 结合

本文中采用在GestureDetector 基础上实现双指缩放或移动,经过GestureDetector 源码查阅,决定在缩放回调基础上进行手指判断。因为缩放手势不仅对Scale 比例 进行了计算和回传,而且对触摸点位置也进行了回传。可以利用Scale 和缩放点判断,触摸点相对增加进行移动判断,减少工作量。

  1. 方案流程
Flutter 双指缩放和移动手势检测系列之--1方案_第5张图片
  1. 效果
    按上述方案,对GestureDetector 更新后,测试效果缩放和移动均和手势跟随效果很好,如下图:
    Flutter 双指缩放和移动手势检测系列之--1方案_第6张图片

源码解读

GestureDetector(
          onScaleStart: (details) {
            if (details.pointerCount >= 2) {
              /// 清空手势判断类型参数
              offset = Offset.zero;
              scaleAccumulatedX = 0;
              scaleAccumulatedY = 0;
              twoTouchMode = TwoTuchMode.twoTouchNoneMode;
            }
          },
          onScaleUpdate: (details) {
            if (details.pointerCount >= 2) {
              if (twoTouchMode == TwoTuchMode.twoTouchNoneMode) {
                twoTouchMode = checkTwoTouchesMoveMode(details);
              }

              if (twoTouchMode == TwoTuchMode.twoTouchZoomMode) {
                debugPrint("details.scale:${details.scale}");
                scale = details.scale;
                setState(() {});
              } else if (twoTouchMode == TwoTuchMode.twoTouchMoveMode) {
                offset += details.focalPointDelta;
                setState(() {});
              }
            }
          },

上述代码在接收scaleUpdate 时,如果还没有识别到手势,则先调用 checkTwoTouchesMoveMode 进行判断。已经识别到手势,就按已识别的手势进行缩放处理。

    /// 判断手势类型是缩放还是移动
    /// 缩放类型判断阀值
    double constScaleThreshold = 0.1;
    /// 移动类型判断阀值
    double constPanningThreshold = 20;
    /// 类型判断,移动累计值
    double scaleAccumulatedX = 0;
    double scaleAccumulatedY = 0;
    TwoTuchMode checkTwoTouchesMoveMode(ScaleUpdateDetails touches) {
      if ((1 - touches.scale).abs() >= constScaleThreshold) {
        debugPrint("two touch scale mode:${(1 - touches.scale).abs()}");
        return TwoTuchMode.twoTouchZoomMode;
      }

      scaleAccumulatedX += touches.focalPointDelta.dx;
      scaleAccumulatedY += touches.focalPointDelta.dy;

      if (scaleAccumulatedX.abs() >= constPanningThreshold ||
          scaleAccumulatedY.abs() >= constPanningThreshold) {
        debugPrint(
            "two touch move mode dx:${scaleAccumulatedX.abs()} , dy:${scaleAccumulatedY.abs()}");
        return TwoTuchMode.twoTouchMoveMode;
      }

      return TwoTuchMode.twoTouchNoneMode;
    }

checkTwoTouchesMoveMode 进行缩放或是移动动作判断,缩放判断优先,如果超过阀值constScaleThreshold识别为缩放手势;横行或是纵向移动距离超过阀值 constPanningThreshold 识别为移动手势。

方案小结

小结

测试下来操作性还可以,代码在GestureDetector 基础上构建,也不复杂。

局限

代码没有进行过封装,与使用该缩放功能的代码混合编码,如果要跨项目使用,不便于功能更迭。

代码

本例代码在本文中全部打包提供。
环境:Flutter 3.10.6

示例项目体验

示例第一个截图,可以在应用商店搜索七彩涂色体验,由Flutter 构建的涂色应用。

下一篇

Flutter 双指缩放和移动手势检测系列之–2封装 敬请期待

你可能感兴趣的:(flutter,前端)