React-Native手势识别(曲线比较)

本文介绍了一种用于识别自定义手势的算法实现,该算法通过标准化处理、缩放归一化等步骤来比较用户输入的手势与预设的手势,最终计算出两者之间的相似度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考:http://blog.youkuaiyun.com/pz789as/article/details/52701183


最近在项目中需要用到一个曲线对比,也就是在屏幕上画一条线,然后和预制的手势曲线比较,看看用户是不是使用的这个手势。

这个让我想到以前在做Unity的时候,找到过一个类似的插件,专门用来识别一些特殊自定义的手势,于是乎就将那个插件的代码移植到RN上来,方便以后使用。

看了看源码,大概的意思是两条线分别标准化成32个点的离散点集,然后缩放归一化,最后计算每个点最近的距离,得到总距离除以32,循环一遍,从里面得到最小值,也就是最接近的值。

这个值一般在2到3左右内算是比较接近的,否则就是不相似,当然可以根据自己的需求去定这个阈值。

下面看代码:

我所有的代码写在一个变量里面,所以用的时候直接使用自己里面的方法

var Utils = {
  NormalizedPointsCount: 32,
  blnGesture: false,
  matched: [],
  CompareGesture: function(line1, line2){//比较手势,需要循环比较
    Utils.blnGesture = true;
    var nLine1 = Utils.Normalize(line1);
    var nLine2 = Utils.Normalize(line2);
    var e = 0.5;
    var step = Math.floor(Math.pow(nLine1.length, 1-e));
    var min = Number.MAX_VALUE;
    for(var i=0;i<nLine1.length;i+=step){
      var d1 = Utils.CloundDistance(nLine1, nLine2, i);
      var d2 = Utils.CloundDistance(nLine2, nLine1, i);
      min = Math.min(min, d1, d2);
    }
    return min;
  },
  CloundDistance: function(points1, points2, startIndex){//以一个数组中的一个起点为标准开始循环比较
    var numPoints = points1.length;
    Utils.ResetMatched(numPoints);
    if (points1.length != points2.length){
      console.warn('CloundDistance points1 count != points2 count');
      return Number.MAX_VALUE;
    }
    var sum = 0;
    var i = startIndex;
    do{
      var index = -1;
      var minDistance = Number.MAX_VALUE;
      for(var j=0;j<numPoints;j++){
        if (!Utils.matched[j]){
          var distance = Utils.DisP(points1[i], points2[j]);
          if (distance < minDistance){
            minDistance = distance;
            index = j;
          }
        }
      }
      Utils.matched[index] = true;
      var weight = 1 - ((i - startIndex + points1.length)%points1.length)/points1.length;
      sum += weight * minDistance;
      i = (i+1)%points1.length;
    }while(i != startIndex);
    return sum;
  },
  ResetMatched: function(count){//设置临时匹配数组,用于是否已经比较完毕的标识
    Utils.matched = [];
    for(var i=0;i<count;i++){
      Utils.matched.push(false);
    }
  },
  Normalize: function(points){//标准化处理
    return Utils.Apply(points, Utils.NormalizedPointsCount);
  },
  Apply: function(inputPoints, normalizedPointsCount){
    var normalizedPoints = Utils.Resample(inputPoints, normalizedPointsCount);//对线重新取样
    if (Utils.blnGesture){
      Utils.Scale(normalizedPoints);
      Utils.TranslateToOrigin(normalizedPoints);
    }
    return normalizedPoints;
  },
  Resample: function(points, normalizedPointsCount){//按照设点的数量标准化取样
    normalizedPointsCount = Math.max(3, normalizedPointsCount);
    var intervalLength = Utils.PathLength(points) / (normalizedPointsCount-1);
    var D = 0;
    var q = {x:0, y:0};
    var normalizedPoints = [];
    normalizedPoints.push(points[0]);
    var pointBuffer = [];
    pointBuffer = pointBuffer.concat(points);
    for(var i=1;i<pointBuffer.length;i++){
      var a = pointBuffer[i-1];
      var b = pointBuffer[i];
      var d = Utils.DisP(a, b);//两点的距离
      if ((D+d) > intervalLength){
        q = Utils.LerpP(a, b, (intervalLength - D) / d);
        normalizedPoints.push(q);
        pointBuffer.splice(i, 0, q);
        D = 0;
      }else{
        D += d;
      }
    }
    if (normalizedPoints.length == normalizedPointsCount - 1){
      normalizedPoints.push(pointBuffer[pointBuffer.length - 1]);
    }
    return normalizedPoints;
  },
  PathLength: function(points){//计算点集总长度
    var d=0;
    for(var i=1;i<points.length;i++){
      d += Utils.DisP(points[i-1], points[i]);//得到两点的距离
    }
    return d;
  },

  Scale: function(points){//缩放点数组到标准尺寸
    var side = Utils.MaxSize(points);//得到数组里面x,y的最大和最小值
    var size = Math.max(side.maxX - side.minX, side.maxY - side.minY);
    var invSize = 1 / size;
    for(var i=0;i<points.length;i++){
      var p = points[i];
      p = Utils.PMulV(Utils.PSubP(p, {x:side.minX, y:side.minY}), invSize);//点相减在相乘
      points[i] = p;
    }
  },



最后使用的时候,调用Utils.CompareGesture即可,得到的值自己做判断。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏小败在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值