4

I use the following script in order to get a list of overlapping DIV elements at mouse click coordinates.

In this example script works fine if the DIV are not rotated:

http://jsfiddle.net/eyxt2tt1/2/

If I apply a rotation on a DIV and user click (near red mark in this example)

http://jsfiddle.net/eyxt2tt1/3/

The script will always return two DIV when instead it should consider only one.

I need to know:

  • How to consider the rotation in my calculation?
  • Do you know a better approach/alternatives?

Notes:

  • I cannot use Document.elementFromPoint()
  • They are similar question on SO, but I consider this one different as I need to solve the "rotation issue" in particular.
  • I would appreciate your ideas in the jsfiddle

$(document).click(function (e) {
    var hitElements = getHitElements(e);
    var output = $('#output');
    output.html('');
    for (var i = 0; i < hitElements.length; ++i) {
        output.html(output.html() + '<br />' + hitElements[i][0].tagName + ' ' + hitElements[i][0].id);

    };

});

var getHitElements = function (e) {
    var x = e.pageX;
    var y = e.pageY;
    var hitElements = [];
    $(':visible').each(function () {
        console.log($(this).attr("id"), $(this).outerWidth());
        var offset = $(this).offset();
        console.log('+++++++++++++++++++++');
        console.log('pageX: ' + x);
        console.log('pageY: ' + y);
        console.log($(this).attr("id"), $(this).offset());
        console.log('+++++++++++++++++++++');
        if (offset.left < x && (offset.left + $(this).outerWidth() > x) && (offset.top < y && (offset.top + $(this).outerHeight() > y))) {
            console.log('included: ', $(this).attr("id"));
            console.log('from 0p far x: ', $(this).attr("id"), offset.left + $(this).outerWidth());
            console.log('from 0p far y: ', $(this).attr("id"), offset.top + $(this).outerHeight());
            hitElements.push($(this));
        }
    });

    return hitElements;

}
GibboK
  • 71,848
  • 143
  • 435
  • 658
  • 1
    i think you need to get the transformed left/top values, see this answer http://stackoverflow.com/questions/11229040/how-to-get-the-position-of-element-transformed-with-css-rotate – epoch Apr 28 '15 at 09:22
  • you will need to do smoething like div area intersection – madalinivascu Apr 28 '15 at 09:28
  • thanks all, please look at NOTES for my question, unfortunately I cannot use document.elementFromPoint(x,y) – GibboK Apr 28 '15 at 09:41
  • of interest: https://css-tricks.com/get-value-of-css-rotation-through-javascript/ – GibboK Jul 29 '15 at 07:37

2 Answers2

2

You have to use linear algebra.

I've modified your fiddle - http://jsfiddle.net/pcdk2s0g/

var getHitElements = function (e) {
    var mx = e.pageX;
    var my = e.pageY;
    var hitElements = [];

    $(':visible').each(function () {

        //Parse CSS matrix
        var matrix = [];
        var matrixVal = $(this).css('transform');

        if(matrixVal != "none"){
            var matrixParsed = matrixVal.substr(7, matrixVal.length - 8).split(',');
            for(var i in matrixParsed) matrix[i] = parseFloat(matrixParsed[i]);
        } else {
            matrix = [1, 0, 0, 1, 0, 0];
        }

        var hW = this.offsetWidth / 2; //Half of width
        var hH = this.offsetHeight / 2; //Half of height
        var o = { x: this.offsetLeft + hW, y: this.offsetTop + this.offsetHeight / 2} //Transform origin

        //Define shape points and transform by matrix
        var p1 = {
            x: o.x + matrix[0] * -hW + matrix[2] * -hH + matrix[4],
            y: o.y + matrix[1] * -hW + matrix[3] * -hH + matrix[5]
        }; //Left top

        var p2 = {
            x: o.x + matrix[0] * +hW + matrix[2] * -hH + matrix[4],
            y: o.y + matrix[1] * +hW + matrix[3] * -hH + matrix[5]
        }; //Right top

        var p3 = {
            x: o.x + matrix[0] * +hW + matrix[2] * +hH + matrix[4],
            y: o.y + matrix[1] * +hW + matrix[3] * +hH + matrix[5]
        }; //Right bottom

        var p4 = {
            x: o.x + matrix[0] * -hW + matrix[2] * +hH + matrix[4],
            y: o.y + matrix[1] * -hW + matrix[3] * +hH + matrix[5]
        }; //Left bottom

        //Calculate edge normal vectors & C vars
        var v1 = { x: -(p2.y - p1.y), y: (p2.x - p1.x) }; //Top
        var v2 = { x: -(p3.y - p2.y), y: (p3.x - p2.x) }; //Right
        var v3 = { x: -(p4.y - p3.y), y: (p4.x - p3.x) }; //Bottom
        var v4 = { x: -(p1.y - p4.y), y: (p1.x - p4.x) }; //Left

        var c1 = -(v1.x * p1.x + v1.y * p1.y);
        var c2 = -(v2.x * p2.x + v2.y * p2.y);
        var c3 = -(v3.x * p3.x + v3.y * p3.y);
        var c4 = -(v4.x * p4.x + v4.y * p4.y);

        //Check cursor distance from edge using general line quation: ax + by + c = 0
        var isInner = function(v, c, x, y){
            return (v.x * x + v.y * y + c) / Math.sqrt( v.x*v.x + v.y*v.y )  > 0;
        }

        //Check if mouse point is in shape coords using general line equation
        if(isInner(v1, c1, mx, my) && isInner(v2, c2, mx, my) && isInner(v3, c3, mx, my) && isInner(v4, c4, mx, my))        
            hitElements.push($(this));

    });

    return hitElements;

}

It uses CSS transform property which is translated to CSS matrix by browser.

Code parses CSS matrix, calculates new edge points and checks if cursor is in translated element.

It works with any angle or CSS transformation.

Code can be improved, for example you can parse CSS transform-origin property too.

Jiří Hýbek
  • 381
  • 1
  • 9
  • Look very cool, I will test it and let you know. Would you able to manage also the transform-origin property? How? What could you suggest? Many thanks for your expertise on this. – GibboK Apr 28 '15 at 13:34
  • 1
    Updated - now supports transform-origin property - http://jsfiddle.net/pcdk2s0g/3/ – Jiří Hýbek Apr 28 '15 at 13:57
  • Just one last thing, could you please add some links with more information of the Math involved?... for example I don't get the point commented as ..."& C vars"".. and variable hW.. many thanks in advance for your support. – GibboK Apr 28 '15 at 14:20
  • 1
    Linear equation, used for detection (checking position of cursor against edge line, C vars) - http://en.wikipedia.org/wiki/Linear_equation (see General form); Understanding CSS transformation matrix - https://dev.opera.com/articles/understanding-the-css-transforms-matrix/ ; hW & hH (or wA, wB, hA, hB in new fiddle) variables are used to calculate absolute shape points (vertices) ... – Jiří Hýbek Apr 28 '15 at 16:36
1

If you rotate only in steps of 90°, then you can use element.getBoundingClientRect() or the code from this question: How to get the position of element transformed with css rotate

If you rotate by arbitrary angles, you will need to calculate the corner points of the rectangle to get a four sided polygon. You can then use this answer: JS- Check if Point Inside A Polygon?

Community
  • 1
  • 1
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820