如何检测用户是否使用 canvas 和 javascript 在触摸设备上绘制了一个圆圈?

Ram*_*ind 5 javascript algorithm math geometry canvas

我正在使用 Javascript 创建一个七巧板益智游戏。我需要检测用户何时用手指画了一个圆圈(或类似圆形的形状)。我已经能够收集数百(如果不是数千)x 和 y 点:

 var touchX = event.targetTouches[0].pageX - canvas.offsetLeft;
 var touchY = event.targetTouches[0].pageY - canvas.offsetTop;
Run Code Online (Sandbox Code Playgroud)

然后我将每个 x 和 y 坐标推入一个数组:

    touchMoveX.push(touchX);   
    touchMoveY.push(touchY);
Run Code Online (Sandbox Code Playgroud)

然后我遍历每个数组并创建两个点:

    for(var i = 0; i < touchMoveX.length; i++)
      {
        for(var l=0; l < touchMoveY.length; l++)
        {
            var xPosition = touchMoveX[i];
            var yPosition = touchMoveY[l];

            var v1x = touchMoveX[i];
            var v2x = touchMoveX[i + 1];
            var v1y = touchMoveY[l];
            var v2y = touchMoveY[l + 1];
Run Code Online (Sandbox Code Playgroud)

然后使用这两个点,我使用以下公式计算出这两个点之间的角度(以度为单位):

 var v1 = {x: v1x, y: v1y}, v2 = {x: v2x, y: v2y},
 angleRad = Math.acos( (v1.x * v2.x + v1.y * v2.y) / 
 (Math.sqrt(v1.x*v1.x + v1.y*v1.y) * Math.sqrt(v2.x*v2.x + v2.y*v2.y) ) ),
 angleDeg = angleRad * 180 / Math.PI;
Run Code Online (Sandbox Code Playgroud)

然后我总结所有的角度,看看它们是否在 360 度左右。

但是我描述的上面的代码运行得不是很好。有没有人有更好的方法来做到这一点?非常感谢。

Win*_*tro 5

是的,计算所有点的平均值(给你一个便宜的近似中心)然后检查是否超过一定百分比的点在某个阈值内。您可以调整这些值以调整精度,直到感觉正确为止。

编辑:没有考虑到圆可以有多种尺寸,但您可以添加另一个步骤来计算所有距离的平均值。为此调整了示例。

var totalAmount = touchMoveX.length;

// sum up all coordinates and divide them by total length 
// the average is a cheap approximation of the center.
var averageX = touchMoveX.reduce( function ( previous, current) {
    return previous + current;
} ) / totalAmount ;
var averageY = touchMoveY.reduce( function ( previous, current) {
    return previous + current;
} ) / totalAmount ;

// compute distance to approximated center from each point
var distances = touchMoveX.map ( function ( x, index ) {
    var y = touchMoveY[index];        
    return Math.sqrt( Math.pow(x - averageX, 2) + Math.pow(y - averageY, 2) );
} );
// average of those distance is 
var averageDistance = distances.reduce ( function ( previous, current ) {
    return previous + current;
} ) / distances.length;

var min = averageDistance * 0.8;
var max = averageDistance * 1.2;
// filter out the ones not inside the min and max boundaries 
var inRange = distances.filter ( function ( d ) {
   return d > min && d < max;
} ).length;

var minPercentInRange = 80;
var percentInRange = inRange.length / totalAmount * 100;
// by the % of points within those boundaries we can guess if it's circle
if( percentInRange > minPercentInRange ) { 
   //it's probably a circle
}
Run Code Online (Sandbox Code Playgroud)