HTML5 canvas: выяснить, находятся ли координаты кликов внутри данного прямоугольника - PullRequest
5 голосов
/ 04 июля 2010

Может быть, у кого-то был подобный опыт по этой дилемме, и он может помочь мне здесь ...

По сути, у меня есть элемент canvas, на котором я рисую несколько прямоугольников в цикле, используя

context.fillRect (x, y, width, height)

Теперь я хочу, чтобы некоторые прямоугольники были горячими точками и отвечали на события щелчка. Я могу узнать точные (x, y) события щелчка, используя event.layerX и event.layerY.

Учитывая, что я знаю следующее:

  • точный х, у клика
  • x, y, ширина и высота каждого прямоугольника

как узнать, произошло ли событие щелчка внутри периметра определенного прямоугольника или нет ?
и
в каком прямоугольнике произошло событие щелчка 0n?

Есть ли математическая формула для этого?

Любая помощь будет высоко ценится, и если я не достаточно ясен, дайте мне знать ...

Спасибо

EDIT
Нет ли лучшего способа, чем перебрать все прямоугольники и проверить их положение и размеры?

Ответы [ 5 ]

12 голосов
/ 16 ноября 2010

Это общая проблема топологии, с которой часто сталкиваются при использовании карт.

Алгоритм определения, находится ли точка в ЛЮБОМ многоугольнике (прямоугольники, круги, неправильные формы), выглядит следующим образом:

  • От проверяемой точки постройте любую линию в любом направлении до края области экрана, в которой расположены ваши полигоны

  • , если линия пересекает любую границу полигона в нечетное количество мест, тогда * в пределах этого многоугольника

  • , если линия пересекает любую границу многоугольника и четное количество мест вне этого многоугольника.

           ------------
           |           |
           |        o--|-------------------    1 intersection: inside
           |           |
           -------------
           ------------
           |           |
    o------|-----------|-------------------    2 intersections: outside
           |           |
           -------------
    

Примечания:

  • Направление линиине имеет значения

  • Не будет работать, если многоугольник обрезается сбоку экрана без закрытия

  • Если многоугольник разрезает сам себя, тогда алгоритмможет потерпеть неудачу, если линия проходит через разрезмазь (например, на рисунке 8 рассматривается как один многоугольник, где линия проходит точно через то место, где соединяются верхняя и нижняя части фигуры)

5 голосов
/ 30 марта 2011

Я ненавижу публиковать здесь тонну кода, но вот класс js, который может вам помочь ...

function Point(x,y){
    this.x=x;
    this.y=y;
}

function Polygon(){
    this.points=[];
    this.x_min=undefined;
    this.x_max=undefined;
    this.y_min=undefined;
    this.y_max=undefined;

    this.add = function(p){
        this.points=this.points.concat(p);
        if (p.x<this.x_min){this.x_min=p.x;}
        if (p.x>this.x_max){this.x_max=p.x;}
        if (p.y<this.y_min){this.y_min=p.y;}
        if (p.y>this.y_min){this.y_max=p.y;}
    }

    this.pointInPoly = function(p){
        var j=(this.points.length-1);  //start by testing the link from the last point to the first point
        var isOdd=false;

        //check the bounding box conditions
        if (p.x < this.x_min || p.x > this.x_max || p.y < this.y_min || p.y > this.y_max){
            return false;
        }

        //if necessary use the line crossing algorithm
        for(var i=0; i<this.points.length; i++){
            if ((this.points[i].y<p.y && this.points[j].y>=p.y) ||  
                (this.points[j].y<p.y && this.points[i].y>=p.y)) {
                    if (this.points[i].x+(p.y-this.points[i].y)/(this.points[j].y-
                        this.points[i].y)*(this.points[j].x-this.points[i].x)<p.x)
                    { isOdd=(!isOdd);} }
            j=i;
        }
        return isOdd;
    }
}

PS: вам нужно преобразовать событие щелчка в локальные координаты, используя что-то вроде следующей функции (где e - событие щелчка):

function getPosition(e){
    var p = new Point();
    if (e.pageX != undefined && e.pageY != undefined) {
        p.x = e.pageX;
        p.y = e.pageY;
     }
     else {
        p.x = e.clientX + document.body.scrollLeft +
                document.documentElement.scrollLeft;
        p.y = e.clientY + document.body.scrollTop +
                document.documentElement.scrollTop;
    }
    p.x -= gCanvas.offsetLeft;
    p.y -= gCanvas.offsetTop;


    return p;
}
1 голос
/ 02 марта 2014

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

function canvasButton(x, y, w, h, style , text) {
  this.style = style
  this.x = x || 0;
  this.y = y || 0;
  this.w = w || 1;
  this.h = h || 1;
  this.fill = style.default.fill ;
  this.text = text || undefined;
  this.onclick = function() {alert("Click! Make sure to add the onclick listener to your button!")}
  this.mouseup = function() {alert("Mouseup! Make sure to add the mouseup listener to your button!")}
  this.addListener = function() {
    var buttonX = this.x;
    var buttonY = this.y;
    var buttonWidth = this.w;
    var buttonHeight = this.h;
    var onclick = this.onclick
    var mouseup = this.mouseup
    var styles = this.style
    var draw = this.draw
    var ctx = this.ctx
    this.ctx.canvas.addEventListener('mousedown',function(e){
        var click_x = e.clientX;
        var click_y = e.clientY;
        if ( click_x >= buttonY && click_x <= buttonX + buttonWidth && click_y >= buttonY && click_y <= buttonY + buttonHeight ) {  
            onclick()
            ctx.fillStyle = style.active.fill
            ctx.fillRect(buttonX, buttonY, buttonWidth, buttonHeight);
        }
    })
    this.ctx.canvas.addEventListener('mouseup',function(e) {
        var click_x = e.clientX;
        var click_y = e.clientY;
        if ( click_x >= buttonY && click_x <= buttonX + buttonWidth && click_y >= buttonY && click_y <= buttonY + buttonHeight ) {  
            mouseup()
            ctx.fillStyle = style.default.fill
            ctx.fillRect(buttonX, buttonY, buttonWidth, buttonHeight);

        }

    })
  }

}

// Draws this shape to a given context
canvasButton.prototype.draw = function(ctx) {
  this.ctx = ctx
  ctx.fillStyle = this.fill;
  ctx.font = this.font
  ctx.fillRect(this.x, this.y, this.w, this.h);
  ctx.fillText(this.text,this.x,this.y)
  this.addListener();
}


//Use it like this:
var start = new canvasButton(x,y,height,width,{ // Styles
    "default": {
        "fill":"#765",

    }, 
    "active": {
        "fill":"#876",

    } 
})
//Event Listeners
start.mousedown = function() {

}
start.mouseup = function() {

}
start.draw(document.getElementById("canvas").getContext("2d")); // Draw to canvas
1 голос
/ 06 июля 2010

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

1 голос
/ 04 июля 2010

Грубо говоря, вы можете сделать это так:

var click_x = event.layerX;
var click_y = event.layerY;

for ( var i = 0; i < rects.length; i++ ) {
    var rect = rects[i];
    if ( click_x >= rect.x && click_x <= rect.x + rect.width
    &&   click_y >= rect.y && click_y <= rect.y + rect.height ) {
        // The click was inside the rectange
    }
}

Предполагается, что мы имеем дело с неотрицательными значениями ширины и высоты:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...