Нахождение элемента, ближайшего к нажатой точке - PullRequest
8 голосов
/ 06 сентября 2011

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

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

ОБНОВЛЕНИЕ

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

Nearest

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

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

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

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

Ответы [ 6 ]

3 голосов
/ 19 октября 2013

Добавление относительно нового 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;
            }
       });
}
3 голосов
/ 09 сентября 2011

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

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

// 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;
}
2 голосов
/ 06 сентября 2011

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

0 голосов
/ 07 сентября 2011

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

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

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

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

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

0 голосов
/ 07 сентября 2011

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

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

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

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

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

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

0 голосов
/ 06 сентября 2011

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

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

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

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

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

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

...