【问题标题】:Strengthen unit-test for "get click coordinates on canvas element"加强“在画布元素上获取点击坐标”的单元测试
【发布时间】:2015-04-15 16:09:37
【问题描述】:

编辑:我最好重写这个问题,我可以看到措辞不好。非常值得一票否决...道歉。

当用户在画布元素内单击时,我试图获取 x,y 坐标。

我相信我有一个正确的解决方案。我已经构建了一个单元测试,我的解决方案通过了。

我要问的是是否可以构建一个我的解决方案失败的单元测试。也许通过 CSS 缩放 canvas 元素、封闭在 iframe 中等。这个问题是关于加强单元测试。

由于我是 Web 开发的新手,我猜有些尴尬的场景是我目前无法预料的。

这是我的单元测试:

function getNumericStyleProperty(style, prop){
    return parseInt(style.getPropertyValue(prop),10) ;
}

function element_position(e) {
    var x = 0, y = 0;
    var inner = true ;
    do {
        x += e.offsetLeft;
        y += e.offsetTop;
        var style = getComputedStyle(e,null) ;
        var borderTop = getNumericStyleProperty(style,"border-top-width") ;
        var borderLeft = getNumericStyleProperty(style,"border-left-width") ;
        y += borderTop ;
        x += borderLeft ;
        if (inner){
          var paddingTop = getNumericStyleProperty(style,"padding-top") ;
          var paddingLeft = getNumericStyleProperty(style,"padding-left") ;
          y += paddingTop ;
          x += paddingLeft ;
        }
        inner = false ;
    } while (e = e.offsetParent);
    return { x: x, y: y };
}

var c = document.getElementById('c');
var t = c.getContext('2d');
t.font = '10px monospace';
c.addEventListener('click', function(e) {
    t.fillStyle = "white"
    t.fillRect(0, 0, c.width, c.height);
    t.fillStyle = "black"

    t.fillText('page:     ' + e.pageX 
                     + ", " + e.pageY, 16, 16*1 );
    
    var bcr = e.target.getBoundingClientRect();
    t.fillText('BoundingClientRect: ' + bcr.left
                               + ", " + bcr.top , 16, 16*2);
    
    // Method 1
    var style = getComputedStyle(e.target,null) ;

    var borderTop = getNumericStyleProperty(style,"border-top-width") ;
    var borderLeft = getNumericStyleProperty(style,"border-left-width") ;
    var paddingTop = getNumericStyleProperty(style,"padding-top") ;
    var paddingLeft = getNumericStyleProperty(style,"padding-left") ;

    t.fillText('client :  ' + e.clientX
                     + ", " + e.clientY, 16, 16*3);

    t.fillText('bcr.x-client-border-padding:  ' 
               + (e.clientX - bcr.left - borderLeft - paddingLeft)
        + ", " + (e.clientY - bcr.top  - borderTop - paddingTop), 16, 16*4);
    
    // Method 2
    var p = element_position(e.target);
    t.fillText('element_position:  ' + p.x
                              + ", " + p.y, 16, 16*5); 

    t.fillText('e.page - element_position():  ' 
               + (e.pageX - p.x)
        + ", " + (e.pageY - p.y), 16, 16*6);
       
}, false);
body {
    margin:     1em; 
    background: #8888ff; 
    padding:    1em;
}

.myDiv {
    position:   absolute;
    left:       20px; 
    top:        40px; 
    border:     solid #88ff88 10px; 
    background: green; 
    margin:     1em; 
    padding:    0.5em;
}

canvas {
    border:     solid red 20px;
    position:   relative; 
    left:       20px; 
    top:        0px;
    padding:    10px;  
    background: brown;  
    margin:     1em
}
<p style="background:white">body</p>
<div class="myDiv">
    <p style="background:white">myDiv</p>
        
    <canvas id="c" width="300" height="200" style="cursor:crosshair"> </canvas>
</div>

如何改进这个单元测试?我正在尝试构建最简单的“最尴尬的场景”。

谁能调整它以使其不再返回画布内的正确点击位置?

编辑:原始问题的简介移至此处:

有很多不好的解决方案。互联网似乎充满了 当我谷歌这个时复制粘贴编码。虽然我有解决方案 可以合作,我想更仔细地研究这个问题, 看看是否可以实现稳健的通用解决方案。

以下是我找到的各种解决方案:
getting mouse position relative to content area of an element - 这个问题有一个很好的答案(连同现场示例),它仍然表现出相同的偏移问题。

How do I get the coordinates of a mouse click on a canvas element?

http://miloq.blogspot.in/2011/05/coordinates-mouse-click-canvas.html

Getting cursor position in a canvas without jQuery document.documentElement 这可能是 与 CSS 边距/边框/填充混淆,或者只是一个错误。的一部分 问题是这是一个常见的问题,每个人都尝试过 它,但没有唯一的解决方案。所以有很多混乱。 问题的另一部分是底层浏览器 API 保持 转移。

问题的另一部分是似乎没有人在测试 证明劣质方法失败的设置,这就是 我试图在这里解决。

【问题讨论】:

  • WADR,既然您可以控制编码,那么您的工作就是避免最尴尬的情况!
  • 我有点困惑:“任何人都可以调整它,使其不再返回画布内的正确点击位置吗?” 你don '不想要画布中的正确位置?
  • 请说明您希望点击坐标相对于什么:客户端、文档、屏幕或画布
  • 抱歉,我的沟通不畅。我已经重写了问题!
  • 这类问题可能更适合姐妹网站 CodeReview:codereview.stackexchange.com

标签: html5-canvas mouseclick-event


【解决方案1】:

这是因为你没有关注earlier advices given to you。

当您使用getBoundingClientRect() 时,它将添加边框和填充(如果有)。您必须从结果中减去这些。

建议不要这样做,而是向wrap the canvas in a div 应用边框和填充来代替。在您的代码中实现此功能会导致以下更新。

PS:此外,您在 cmets 中标记为“有效”的代码是否不起作用(请参阅控制台,它既不会在画布上打印任何内容。如果这样做,它会写在前面的顶部代码...)。

function getNumericStyleProperty(style, prop){
    return parseInt(style.getPropertyValue(prop),10) ;
}

function element_position(e) {
    var x = 0, y = 0;
    var inner = true ;
    do {
        x += e.offsetLeft;
        y += e.offsetTop;
        var style = getComputedStyle(e,null) ;
        var borderTop = getNumericStyleProperty(style,"border-top-width") ;
        var borderLeft = getNumericStyleProperty(style,"border-left-width") ;
        y += borderTop ;
        x += borderLeft ;
        if (inner){
          var paddingTop = getNumericStyleProperty(style,"padding-top") ;
          var paddingLeft = getNumericStyleProperty(style,"padding-left") ;
          y += paddingTop ;
          x += paddingLeft ;
        }
        inner = false ;
    } while (e = e.offsetParent);
    return { x: x, y: y };
}

var c = document.getElementById('c');
var t = c.getContext('2d');
t.font = '10px monospace';
c.addEventListener('click', function(e) {
    t.fillStyle = "white"
    t.fillRect(0, 0, c.width, c.height);
    t.fillStyle = "black"

    t.fillText('pageX:     ' + e.pageX, 16, 16);
    t.fillText('pageY:     ' + e.pageX, 16, 16*2);
    
    var bcr = e.target.getBoundingClientRect();
    t.fillText('BoundingClientRect.left: ' + bcr.left, 16, 16*3);
    t.fillText('BoundingClientRect.top:  ' + bcr.top , 16, 16*4);
    
    // fails
    t.fillText('clientX :  ' + e.clientX, 16, 16*5);
    t.fillText('clientY :  ' + e.clientY, 16, 16*6);
    t.fillText('bcr.x-clientX:  ' + (e.clientX - bcr.left), 16, 16*7);
    t.fillText('bcr.y-clientY:  ' + (e.clientY - bcr.top), 16, 16*8);
    
    // works
    var p = element_position(e.eventTarget);
    t.fillText('element_position x:  ' + p.x, 16, 16*9);
    t.fillText('element_position y:  ' + p.y, 16, 16*10); 
    t.fillText('canvas x:  ' + (e.pageX - p.x), 16, 16*11);
    t.fillText('canvas y:  ' + (e.pageY - p.y), 16, 16*12);
       
}, false);
body {
    margin:     1em; 
    background: #8888ff; 
    padding:    1em;
}

.myDiv {
    position:   absolute;
    left:       20px; 
    top:        40px; 
    border:     solid #88ff88 10px; 
    background: green; 
    margin:     1em; 
    padding:    0.5em;
}

.wrapper {
    border:     solid red 20px;
    position:   relative; 
    left:       20px; 
    top:        0px;
    padding:    10px;  
    background: brown;  
    margin:     1em
}
<p style="background:white">body</p>
    <div class="myDiv">
        <p style="background:white">myDiv</p>
        
        <div class="wrapper">
          <canvas id="c" width="200" height="200" style="cursor:crosshair"> </canvas>
        </div>
    </div>

【讨论】:

  • 对此非常抱歉,我太早点击了“发布”按钮,而且我想我一定在您构建此回复的同时进行了重大编辑。我必须小心避免将来这样做。我的想法是创建一个不管任何样式都可以工作的函数。手动减去边框和填充满足我的单元测试(如我的最终问题代码所示)。我要问的是单元测试是否足够强大,或者是否可以设计出更强大的测试。例如,我认为应用 50% 的 CSS 比例会影响结果。
  • @Pi 不用担心。这有可能发生。如果您使用 CSS(缩放等)应用变换,则需要使用位图大小和元素大小之间的差异。我为视频元素回答了这个问题,但它也适用于画布,希望它可以提供帮助(参见第三个代码块):stackoverflow.com/questions/29522895/…
猜你喜欢
  • 2010-09-08
  • 1970-01-01
  • 1970-01-01
  • 2011-03-15
  • 2012-03-07
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 2015-06-17
相关资源
最近更新 更多