Den*_*aia 47 javascript css html5 transform css3
我想检测,其中一个MouseEvent已经发生,在相对于点击的元素坐标.为什么?因为我想在点击的位置添加绝对定位的子元素.
我知道在没有CSS3转换时如何检测它(见下面的描述).但是,当我添加一个CSS3转换,然后我的算法中断,我不知道如何解决它.
我没有使用任何JavaScript库,我想了解简单JavaScript中的工作原理.所以,请不要回答"只使用jQuery".
顺便说一句,我想要一个适用于所有MouseEvents的解决方案,而不仅仅是"点击".并不重要,因为我相信所有鼠标事件都具有相同的属性,因此相同的解决方案应该适用于所有这些事件.
根据DOM Level 2规范,a MouseEvent几乎没有与获取事件坐标相关的属性:
screenX并screenY返回屏幕坐标(原点是用户监视器的左上角)clientX并clientY返回相对于文档视口的坐标.因此,为了找到MouseEvent相对于被点击元素内容的位置,我必须做这个数学运算:
ev.clientX - this.getBoundingClientRect().left - this.clientLeft + this.scrollLeft
Run Code Online (Sandbox Code Playgroud)
ev.clientX 是相对于文档视口的坐标this.getBoundingClientRect().left 是元素相对于文档视口的位置this.clientLeft 是元素边界和内部坐标之间的边界(和滚动条)的数量this.scrollLeft 是元素内部的滚动量getBoundingClientRect(),clientLeft并scrollLeft在CSSOM View Module中指定.
混乱?尝试以下JavaScript和HTML.点击后,红点应该出现在点击发生的确切位置.这个版本"非常简单",按预期工作.
function click_handler(ev) {
var rect = this.getBoundingClientRect();
var left = ev.clientX - rect.left - this.clientLeft + this.scrollLeft;
var top = ev.clientY - rect.top - this.clientTop + this.scrollTop;
var dot = document.createElement('div');
dot.setAttribute('style', 'position:absolute; width: 2px; height: 2px; top: '+top+'px; left: '+left+'px; background: red;');
this.appendChild(dot);
}
document.getElementById("experiment").addEventListener('click', click_handler, false);
<div id="experiment" style="border: 5px inset #AAA; background: #CCC; height: 400px; position: relative; overflow: auto;">
<div style="width: 900px; height: 2px;"></div>
<div style="height: 900px; width: 2px;"></div>
</div>
Run Code Online (Sandbox Code Playgroud)
现在,尝试添加CSStransform:
#experiment {
transform: scale(0.5);
-moz-transform: scale(0.5);
-o-transform: scale(0.5);
-webkit-transform: scale(0.5);
/* Note that this is a very simple transformation. */
/* Remember to also think about more complex ones, as described below. */
}
Run Code Online (Sandbox Code Playgroud)
该算法不知道变换,因此计算错误的位置.更重要的是,Firefox 3.6和Chrome 12之间的结果有所不同.Opera 11.50的行为与Chrome一样.
在这个例子中,唯一的变换是缩放,所以我可以乘以缩放因子来计算正确的坐标.但是,如果我们想任意变换(缩放,旋转,倾斜,翻译,矩阵),甚至嵌套转换(另一转化元素中变换元件),那么我们真的需要一个更好的方式来计算坐标.
Ash*_*kes 14
您遇到的行为是正确的,您的算法不会中断.首先,CSS3变换的设计不会干扰盒子模型.
试着解释一下......
在元素上应用CSS3变换时.元素假设一种相对定位.因为周围的元素不受变形元素的影响.
例如想象一个水平行中的三个div.如果应用缩放变换来减小中心div的大小.周围的div不会向内移动以占据曾经占据变形元素的空间.
例如:http://jsfiddle.net/AshMokhberi/bWwkC/
所以在盒子模型中,元素实际上并没有改变大小.只有它的渲染大小更改.
您还必须记住,您正在应用缩放变换,因此您的元素"实际"大小实际上与其原始大小相同.你只是改变它的感知大小.
解释..
想象一下,你创建一个宽度为1000像素的div并将其缩小到1/2的大小.div的内部大小仍然是1000px,而不是500px.
所以你的点的位置相对于div的"真实"大小是正确的.
我修改了你的例子来说明.
说明
http://jsfiddle.net/AshMokhberi/EwQLX/
因此,为了使鼠标点击坐标与div上的可见位置匹配,您需要了解鼠标根据窗口返回坐标,并且您的div偏移也基于其"实际"大小.
由于您的对象大小是相对于窗口的,唯一的解决方案是使用与div相同的比例值来缩放偏移坐标.
但是,根据您设置div的Transform-origin属性的位置,这可能会变得棘手.因为这将影响抵消.
看这里.
http://jsfiddle.net/AshMokhberi/KmDxj/
希望这可以帮助.
如果元素是容器和位于绝对或相对,则可以在其内部放置相对于亲和宽度= 1px的,高度= 1像素,并移动到容器的内部,并经过元件,它的位置移动各使用document.elementFromPoint(事件. clientX,event.clientY)=))))
您可以使用二进制搜索来加快速度.看起来很糟糕,但它确实有效
http://jsfiddle.net/3VT5N/3/ - 演示
迄今为止最快的。在我的 3d 转换网站上,接受的答案大约需要 40-70 毫秒,这通常需要不到 20 毫秒(小提琴):
function getOffset(event,elt){
var st=new Date().getTime();
var iterations=0;
//if we have webkit, then use webkitConvertPointFromPageToNode instead
if(webkitConvertPointFromPageToNode){
var webkitPoint=webkitConvertPointFromPageToNode(elt,new WebKitPoint(event.clientX,event.clientY));
//if it is off-element, return null
if(webkitPoint.x<0||webkitPoint.y<0)
return null;
return {
x: webkitPoint.x,
y: webkitPoint.y,
time: new Date().getTime()-st
}
}
//make full-size element on top of specified element
var cover=document.createElement('div');
//add styling
cover.style.cssText='height:100%;width:100%;opacity:0;position:absolute;z-index:5000;';
//and add it to the document
elt.appendChild(cover);
//make sure the event is in the element given
if(document.elementFromPoint(event.clientX,event.clientY)!==cover){
//remove the cover
cover.parentNode.removeChild(cover);
//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){
iterations++;
//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(event.clientX,event.clientY);
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(cover);
//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!==cover;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'));
}
//remove all of the elements used for testing
cover.parentNode.removeChild(cover);
//the returned object
var returnObj={
x: correctLeft,
y: correctTop,
time: new Date().getTime()-st,
iterations: iterations
}
return returnObj;
}
Run Code Online (Sandbox Code Playgroud)
并在同一页面中包含以下 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%;
}
Run Code Online (Sandbox Code Playgroud)
它本质上将元素分成 9 个方块,通过 确定点击的是哪一个document.elementFromPoint。然后它将其分成 9 个较小的正方形,依此类推,直到精确到一个像素内。我知道我评论过头了。接受的答案比这慢几倍。
编辑:现在速度更快,如果用户使用 Chrome 或 Safari,它将使用为此设计的本机函数,而不是 9 个扇区的东西,并且可以在不到 2 毫秒的时间内一致完成!
| 归档时间: |
|
| 查看次数: |
23954 次 |
| 最近记录: |