参考: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即可,得到的值自己做判断。