2013-07-30 1 views
4

Метод webkitConvertPointFromPageToNode(in Node node, in WebKitPoint p) является удивительным; дайте ему узел DOM и точку в координатах страницы (скажем, позицию курсора мыши), и она вернет вам координаты в локальную систему координат этого узла. К сожалению, в настоящее время это only available in webkit.Показать javascript реализация webkitConvertPointFromPageToNode

# Choose a node into which we'll map the mouse coordinates 
node = $('#subjectElement').get(0) 

handleMouseMove = (e) -> 
    # Convert the mouse position to a Point 
    mousePoint = new WebKitPoint(e.pageX, e.pageY) 

    # Convert the mouse point into node coordinates using WebKit 
    nodeCoords = webkitConvertPointFromPageToNode(node, mousePoint) 

# Attach a handler to track the mouse position 
$(document).on 'mousemove', handleMouseMove  

Я бросил мою всю математику-мозг на эту проблему, но независимо от того, насколько близко я, моя реализация не разваливается с одним дополнительным уровнем композиции или применения 3D-перспективы.

Пришло время для полифония convertPointFromPageToNode, который работает так же, как и реализация WebKit, в 3D. @4esn0kgave one a shot, но он решает только 2D-кейс.

Можете ли вы написать то, что делает эту работу JSFiddle?

webkitConvertPointFromPageToNode kicking ass and taking names on a complicatedly transformed element

http://jsfiddle.net/steveluscher/rA27K/

+0

Вы в порядке, если он не может вычислить координаты элемента? например он работает только если мышь находится на элементе? – Markasoftware

+0

Я знаю, что это старый вопрос, но можете ли вы взглянуть на него. Я отправил ответ. – Markasoftware

+0

Я разговаривал с моими коллегами о вашем решении на днях, @Markasoftware! Дайте мне время, чтобы успокоиться на работе, и тогда я хорошо посмотрю! – steveluscher

ответ

3

Это кажется удивительным вопрос, но есть ПОЧТИ продублировать здесь: How to get the MouseEvent coordinates for an element that has CSS3 Transform? но никто не смотрит на мой ответ там, и это, кажется, гораздо более общий характер, так что я» ll опубликуйте его здесь, с несколькими изменениями, чтобы сделать его более понятным:

В основном, это работает, делая это: разделите элемент, на который вы пытаетесь найти относительные координаты, и разделите его на 9 меньших элементов. Используйте document.elementFromPoint, чтобы узнать, находится ли координата над этим мини-элементом. Если это так, разделите этот элемент на еще 9 элементов и продолжайте делать это до тех пор, пока не станет достаточно точной координаты. Затем используйте getBoundingClientRect, чтобы найти экранные координаты этого мини-элемента. БУМ!

jsfiddle: http://jsfiddle.net/markasoftware/rA27K/8/

Вот функция JavaScript:

function convertPointFromPageToNode(elt,coords){ 
    ///the original innerHTML of the element 
    var origHTML=elt.innerHTML; 
    //now clear it 
    elt.innerHTML=''; 
    //now save and clear bad styles 
    var origPadding=elt.style.padding=='0px'?'':elt.style.padding; 
    var origMargin=elt.style.margin=='0px'?'':elt.style.margin; 
    elt.style.padding=0; 
    elt.style.margin=0; 
    //make sure the event is in the element given 
    if(document.elementFromPoint(coords.x,coords.y)!==elt){ 
     //reset the element 
     elt.innerHTML=origHTML; 
     //and styles 
     elt.style.padding=origPadding; 
     elt.style.margin=origMargin; 
     //we've got nothing to show, so return null 
     return null; 
    } 
    //array of all places for rects 
    var rectPlaces=['topleft','topcenter','topright','centerleft','centercenter','centerright','bottomleft','bottomcenter','bottomright']; 
    //function that adds 9 rects to element 
    function addChildren(elt){ 
     //loop through all places for rects 
     rectPlaces.forEach(function(curRect){ 
      //create the element for this rect 
      var curElt=document.createElement('div'); 
      //add class and id 
      curElt.setAttribute('class','offsetrect'); 
      curElt.setAttribute('id',curRect+'offset'); 
      //add it to element 
      elt.appendChild(curElt); 
     }); 
     //get the element form point and its styling 
     var eltFromPoint=document.elementFromPoint(coords.x,coords.y); 
     var eltFromPointStyle=getComputedStyle(eltFromPoint); 
     //Either return the element smaller than 1 pixel that the event was in, or recurse until we do find it, and return the result of the recursement 
     return Math.max(parseFloat(eltFromPointStyle.getPropertyValue('height')),parseFloat(eltFromPointStyle.getPropertyValue('width')))<=1?eltFromPoint:addChildren(eltFromPoint); 
    } 
    //this is the innermost element 
    var correctElt=addChildren(elt); 
    //find the element's top and left value by going through all of its parents and adding up the values, as top and left are relative to the parent but we want relative to teh wall 
    for(var curElt=correctElt,correctTop=0,correctLeft=0;curElt!==elt;curElt=curElt.parentNode){ 
     //get the style for the current element 
     var curEltStyle=getComputedStyle(curElt); 
     //add the top and left for the current element to the total 
     correctTop+=parseFloat(curEltStyle.getPropertyValue('top')); 
     correctLeft+=parseFloat(curEltStyle.getPropertyValue('left')); 
    } 
    //reset the element 
    elt.innerHTML=origHTML; 
    //restore element styles 
    elt.style.padding=origPadding; 
    elt.style.margin=origMargin; 
    //the returned object 
    var returnObj={ 
     x: correctLeft, 
     y: correctTop 
    } 
    return returnObj; 
} 

ВАЖНО !!! Кроме того, необходимо включить этот CSS для его работы:

.offsetrect{ 
    position: absolute; 
    opacity: 0; 
    height: 33.333%; 
    width: 33.333%; 
} 
#topleftoffset{ 
    top: 0; 
    left: 0; 
} 
#topcenteroffset{ 
    top: 0; 
    left: 33.333%; 
} 
#toprightoffset{ 
    top: 0; 
    left: 66.666%; 
} 
#centerleftoffset{ 
    top: 33.333%; 
    left: 0; 
} 
#centercenteroffset{ 
    top: 33.333%; 
    left: 33.333%; 
} 
#centerrightoffset{ 
    top: 33.333%; 
    left: 66.666%; 
} 
#bottomleftoffset{ 
    top: 66.666%; 
    left: 0; 
} 
#bottomcenteroffset{ 
    top: 66.666%; 
    left: 33.333%; 
} 
#bottomrightoffset{ 
    top: 66.666%; 
    left: 66.666%; 
} 

ТАКЖЕ: Я изменил немного вашего CSS, давая «дед» Див идентификатор и ссылки на него в вашем CSS, используя #div1 вместо div, потому что мой код генерирует divs, а ваши стили div также применяются к тем, которые использует мой код, и испортили его.

ОДИН ПОСЛЕДНИЙ: Я не знаю CoffeeScript, поэтому я скорректировал ваш код, чтобы сделать его чистым JavaScript. Извини за это.

+1

Мне очень нравится такой подход, ибо его удовольствие, если ни для чего другого. Я бы предпочел увидеть аналитическое решение исходной проблемы (любые 3D-гуру там?), Но точка полифилла - заполнить промежуток временно, пока официальный API не поймает, правильно? Нужно провести большую очистку, но я бы хотел, чтобы это было сделано в формальном лицензионном проекте MIT. Вы заинтересованы в сотрудничестве на одном? – steveluscher

+0

Интересный подход, но я не думаю, что хочу запустить это из прослушивания mousemove. Было бы неплохо использовать математическое решение с поперечным браузером. – hezamu

+0

Я запускаю его из mousemove и не слишком много отставания. Но я нахожусь на настольном компьютере, может быть, другие устройства будут страдать больше – Markasoftware

2

Я написал код типа TypeScript, который выполняет некоторые преобразования: jsidea core library. Его еще не стабильный (pre alpha). Вы можете использовать его так:

Создание вы превращаете экземпляр:

var transformer = jsidea.geom.Transform.create(yourElement); 

Модель коробки вы хотите преобразовать в (по умолчанию: «Граница»):

var toBoxModel = "border"; 

блочная модель где поступают ваши координаты ввода (по умолчанию: «граница»):

var fromBoxModel = "border"; 

Tranform your global c (здесь {x: 50, y: 100, z: 0}) в локальное пространство. Полученная точка имеет 4 компонента: x, y, z и w.

var local = transformer.globalToLocal(50, 100, 0, toBoxModel, fromBoxModel); 

Я реализовал некоторые другие функции, такие как localToGlobal и localToLocal. Если вы хотите попробовать, просто загрузите сборку выпуска и используйте jsidea.min.js.

+0

Я хочу использовать это, чтобы сделать polyfill 'window.convertPointFromPageToNode'. Выходные взломать! – steveluscher

0

У меня есть эта проблема и я начал пытаться вычислить матрицу. я начал библиотеку вокруг него: https://github.com/ombr/referentiel

$('.referentiel').each -> 
    ref = new Referentiel(this) 
    $(this).on 'click', (e)-> 
    input = [e.pageX, e.pageY] 
    p = ref.global_to_local(input) 
    $pointer = $('.pointer', this) 
    $pointer.css('left', p[0]-1) 
    $pointer.css('top', p[1]-1) 

Что вы думаете?

Смежные вопросы