Поиск элемента, ближайшего к точке щелчка

Нужна помощь здесь. Я дизайнер пользовательского интерфейса, который плохо разбирается в числах, занимаюсь экспериментальным дизайном веб-форм, и мне нужно знать, какой элемент ввода находится ближе всего к точке нажатия на веб-странице. Я знаю, как сделать ближайшего соседа с точками, но входные элементы представляют собой прямоугольники, а не точки, поэтому я застрял.

Я использую jQuery. Мне просто нужна помощь с этим маленьким алгоритмом. Как только я закончу свой эксперимент, я покажу вам, ребята, что я делаю.

ОБНОВЛЕНИЕ

Я думал о том, как это может работать. Посмотрите на эту диаграмму:

Ближайший

Каждый прямоугольник имеет 8 точек (точнее, 4 точки и 4 линии), которые являются значимыми. Только значение x имеет значение для горизонтальных точек (красная точка) и только значение y имеет значение для вертикальных точек (зеленая точка). И x, и y значимы для углов.

Оранжевые кресты — это точки, по которым нужно измерять — щелчки мышью в моем случае использования. Светло-фиолетовые линии — это расстояния между оранжевым крестом и его возможной ближайшей точкой.

Итак… для любого заданного оранжевого креста выполните цикл через каждую из 8 точек в каждом прямоугольнике, чтобы найти ближайший край или угол каждого прямоугольника, ближайший к оранжевому кресту. Прямоугольник с наименьшим значением является ближайшим.

Я могу концептуализировать и визуализировать это, но не могу поместить это в код. Помощь!


person Aen Tan    schedule 06.09.2011    source источник
comment
Используйте 4 точки, которые представляют прямоугольник в алгоритме ближайшего соседа. Или используйте центральную точку прямоугольника cx = (слева + ширина)/2, cy = (сверху + высота)/2.   -  person Louis Ricci    schedule 06.09.2011
comment
есть ли шанс, что вы могли бы опубликовать код или что-то сделать на jsfiddle.net? а то все гипотетично...   -  person T9b    schedule 07.09.2011
comment
Добавлена ​​иллюстрация, объясняющая проблему и способы ее решения.   -  person Aen Tan    schedule 07.09.2011


Ответы (6)


Ваш алгоритм правильный. Так как вам нужна помощь в коде, а не в алгоритме, вот код:

Возможно, это не самое эффективное. Но это работает.

// 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;
}
person uzyn    schedule 09.09.2011
comment
Большой. Как раз то, что мне нужно. Как мне сделать код адаптируемым к любой ширине и высоте? - person Aen Tan; 09.09.2011
comment
Он адаптируется к любой ширине и высоте. Вам просто нужно определить размеры кнопок и координаты щелчка в верхней части JavaScript. - person uzyn; 09.09.2011

Добавление относительно нового API elementFromPoint() позволяет Мы используем альтернативный, потенциально более легкий подход: мы можем нажимать test вокруг курсора мыши, двигаясь большими кругами, пока не найдем ближайший элемент.

Здесь я собрал быстрый нерабочий пример: 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;
            }
       });
}
person Dean Hunt    schedule 18.10.2013

Вы можете искать ближайшую угловую точку всех прямоугольников. Это работает в большинстве случаев, быстро и легко реализуется. Пока ваши прямоугольники выровнены по регулярной сетке, этот метод дает вам ближайший прямоугольник.

person rocksportrocker    schedule 06.09.2011

Я бы сделал это не цифрами, а логикой.

Я предполагаю, что вы хотите получить что-то, что говорит: «Если x - ближайший элемент, тогда сделайте что-нибудь, когда я щелкнул в другом месте, затем сделайте что-нибудь с x»

Вы могли бы сделать это, если бы каждый из элементов, с которыми вы хотите что-то сделать, находился в простых контейнерах <div>, которые были больше, чем элемент, который вы хотите обработать, но не больше, чем на полпути между содержащимся в нем объектом и ближайшим к нему объектом. Фактически сетка.

дать всем контейнерам один и тот же класс.

Тогда вы могли бы сказать: «Если щелкнуть y, сделайте что-нибудь с x», вы бы уже знали, какой элемент находится в каждом контейнере.

Я бы написал код, но я ухожу с работы...

person T9b    schedule 06.09.2011

Если вы хотите найти расстояние между двумя точками на 2D-сетке, вы можете использовать следующую формулу:

(для 2D точек A и B)

расстояниеX = A.x - B.x

расстояние Y = A.y - B.y

totalDistance = SquareRoot ((distX * distX) + (distY * distY))

Как только вы сможете проверить расстояние между двумя точками, вы сможете довольно легко определить, какой угол прямоугольника ближе всего к вашему щелчку мыши. Есть множество вещей, которые вы можете сделать, чтобы оптимизировать задуманный вами алгоритм, но это должно дать вам хорошее начало.

person 5hoom    schedule 07.09.2011

лол, вопрос в том, почему вы думаете о формах? ваш вопрос на самом деле звучит так: «если я щелкну координату, найдите ближайший узел/точку к моему щелчку», что связано с прохождением различных узлов и вычислением расстояний.

Если тот же X, используйте разницу y

Если тот же y, используйте разницу x

в противном случае используйте гипотенузу

Как только вы найдете ближайшую точку, вы сможете получить правильную родительскую форму? Это будет работать, потому что вы пытаетесь привязаться к ближайшей точке. Так что он будет работать даже с причудливыми формами, такими как звезды.

person Gary    schedule 07.09.2011