找到最接近点击点的元素

8

需要帮助。我是一个不擅长数字的UI设计师,正在进行实验性网页表单设计,并且我需要知道哪个输入元素与网页上单击的点最接近。我知道如何使用点进行最近邻居计算,但是输入元素是矩形而不是点,所以我卡住了。

我正在使用jQuery。我只需要这个小算法方面的帮助。一旦我完成我的实验,我会向大家展示我正在做什么。

更新

我考虑了它可以如何工作。看一下这个图:

Nearest

每个矩形都有8个点(或者更确切地说是4个点和4条线)非常重要。对于水平点(红点),只有x值很重要;对于垂直点(绿点),只有y值很重要。对于角落,x和y都很重要。

橙色十字是要测量的点-在我的用例中是鼠标单击。浅紫色的线是橙色十字与其可能最近点之间的距离。

因此...对于任何给定的橙色十字,循环遍历每个矩形的8个点,以找到最接近橙色十字的每个矩形的边缘或角落。具有最低值的矩形是最近的一个。

我可以理解和想象它,但无法用代码实现。救命!


在最近邻算法中,使用代表矩形的4个点。或者使用矩形的中心点cx = (left + width)/2, cy = (top + height)/2。 - Louis Ricci
你能否贴一些代码或在jsfiddle.net上做些什么?否则这都有点虚无缥渺的... - T9b
添加了一张插图来说明问题以及可能的解决方法。 - Aen Tan
6个回答

3

你的算法是正确的。由于你需要帮助的是代码而不是算法,这里提供代码:

它可能不是最高效的,但它可以工作。

// Define the click
var click = Array(-1, -2); // coodinates in x,y

// Define the buttons
// Assuming buttons do not overlap
var button0 = Array(
    Array(0, 0), // bottom-left point (assuming x is horizontal and y is vertical)
    Array(6, 6) // upper-right point
);

var button1 = Array(
    Array(10, 11),
    Array(17, 15)
);

var button2 = Array(
    Array(-8, -5),
    Array(-3, -1)
);

// Which button to trigger for a click
i = which(click, Array(button0, button1, button2));
alert(i);


function which(click, buttons){
    // Check if click is inside any of the buttons
    for (i in buttons){
        var button = buttons[i];
        var bl = button[0];
        var tr = button[1];

        if ( (click[0] >= bl[0] && click[0] <= tr[0]) &&
             (click[1] >= bl[1] && click[1] <= tr[1]) ){
            return i;
        }
    }

    // Now calculate distances
    var distances = Array();

    for (i in buttons){
        var button = buttons[i];
        var bl = button[0];
        var tr = button[1];

        if ( (click[0] >= bl[0] && click[0] <= tr[0])) {
            distances[i] = Math.min( Math.abs(click[1]-bl[1]), Math.abs(click[1]-tr[1]) );
        }
        else if ( (click[1] >= bl[1] && click[1] <= tr[1])) {
            distances[i] = Math.min( Math.abs(click[0]-bl[0]), Math.abs(click[0]-tr[0]) );
        }
        else{
            distances[i] =  Math.sqrt(
                                (Math.pow(Math.min( Math.abs(click[0]-bl[0]), Math.abs(click[0]-tr[0]) ), 2)) +
                                (Math.pow(Math.min( Math.abs(click[1]-bl[1]), Math.abs(click[1]-tr[1]) ), 2))
                            );
        }
    }

    var min_id = 0;
    for (j in distances){
        if (distances[j] < distances[min_id]){
            min_id = j;
        }
    }

    return min_id;
}

太好了,正是我需要的。我该如何使代码适应任何宽度和高度? - Aen Tan
1
它可以适应任何宽度和高度。您只需要在JavaScript的顶部定义按钮的尺寸和点击坐标即可。 - uzyn

3

相对较新的elementFromPoint()API的添加使我们可以采用另一种可能更轻量级的方法:我们可以在鼠标光标周围进行命中测试,沿着更大的圆圈前进,直到找到最近的元素。

我在这里提供了一个快速的非生产示例:http://jsfiddle.net/yRhhs/(仅限Chrome / Safari,因为使用了webkitMatchesSelector)。由于用于可视化算法的点数,性能可能会变得很慢。

代码的核心部分,在轻量级性能优化和事件绑定之外,就是这段:

function hitTest(x, y){
    var element, i = 0;
    while (!element){
        i = i + 7; // Or some other threshold.

        if (i > 250){ // We do want some safety belts on our while loop.
            break;
        }

        var increment = i/Math.sqrt(2);
        var points = [
            [x-increment, y-increment], [x+increment, y-increment],
            [x+increment, y+increment], [x-increment, y+increment]
        ];

        // Pop additional points onto the stack as the value of i gets larger.
        // ...

        // Perhaps prematurely optimized: we're using Array.prototype.some to bail-out
        // early once we've found a valid hit target.
        points.some(function(coordinates){
            var hit = document.elementFromPoint.apply(document, coordinates); 
            // isValidHit() could simply be a method that sees whether the current
            // element matches the kinds of elements we'd like to see.
            if (isValidHit(hit)){
                element = hit;
                return true;
            }
       });
}

2
你可以寻找所有矩形的最近角点。这种方法在大多数情况下都有效,并且速度快,易于实现。只要你的矩形在一个规则的网格上排列,这种方法就能给出最近的矩形。

0
我会用逻辑而不是数字来解决这个问题。
我假设你想要得到的结果是:“如果x是最近的元素,那么在我点击其他地方时对x进行某些操作”。
如果你想要对每个元素进行操作,可以将它们放在简单的
容器中,这些容器比你想要处理的元素大,但不大于它所包含的对象和它下一个最近的对象之间的一半。实际上就像一个网格。
给所有容器相同的类。
然后你可以说,“如果y被点击,去对x进行某些操作”,你已经知道每个容器中的元素是哪个了。
我会写代码,但我要离开工作了...

0
如果你想要在二维网格上找到两点之间的距离,可以使用以下公式:
(对于二维点A和B)
distanceX = A.x - B.x
distanceY = A.y - B.y
totalDistance = squareRoot ((distX * distX) + (distY * distY))
一旦你能够检查出两点之间的距离,你就可以很容易地确定你的鼠标点击最接近哪个矩形角落。有许多优化算法的方法,但这应该能给你一个良好的起点。

0

哈哈,问题是你为什么考虑形状?

你的问题实际上是:“如果我点击一个坐标,找到最近的节点/点”,这需要遍历各种节点并计算距离。

如果X相同,则使用Y差异

如果y相同,则使用x差异

否则使用斜边

一旦您找到最近的点,您就可以得到父形状,对吧?

这将起作用,因为您正在尝试捕捉最近的点。所以它甚至可以与星星等花哨的形状一起使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接