HTMLElement.offsetTop不能以亚像素精度工作

时间:2018-03-27 05:30:10

标签: javascript google-chrome

我正在编写一个小程序,我以指定的速度移动DOM

当我以20px per second的速度移动时,添加到offset的{​​{1}}约为elem.style.top

问题是,当0.3px per frame小于offset时,0.5px无法移动!

我构建了一个简化的示例,可以在我的程序中演示该问题:



elem

var requestFrameAnimationId;

function myMove(offset) {
  var elem = document.getElementById("animate");   
  requestFrameAnimationId = animationLoop(frame);
  function frame() {
    console.log(elem.offsetTop);
    if (elem.offsetTop === 350) {
      cancelAnimationFrame(requestFrameAnimationId);
    } else {
      elem.style.top = elem.offsetTop + offset + 'px'; 
      elem.style.left = elem.offsetLeft + offset + 'px'; 
    }
  }
}

function animationLoop(render) {
    var running, lastFrame = +new Date(); // casting Date to Number
    function loop(now) {`enter code here`
      requestFrameAnimationId = requestAnimationFrame(loop);
      running = render(now - lastFrame);
      lastFrame = now;
    }
    loop(lastFrame);
}

#container {
  width: 400px;
  height: 400px;
  position: relative;
  background: yellow;
}
#animate {
  width: 50px;
  height: 50px;
  position: absolute;
  background-color: red;
}




尝试点击<!DOCTYPE html> <html> <body> <p> <button onclick="myMove(0.3)">Move at 0.3px per frame</button> <button onclick="myMove(0.5)">Move at 0.5px per frame</button> </p> <div id="container"> <div id="animate"></div> </div> </body> </html>。矩形应该移动。 点击Move at 0.5px per frame重置它。

现在尝试点击Run code snippet。 它应该更慢地移动DOM,但你可以看到DOM没有移动。

这很奇怪,因为当我最初跟踪javascript变量Move at 0.3px per frame中的top位置,并将topPos应用于${topPos + offset}时,它在甚至更慢的速度!

所以我的猜测是elem.style.top舍入小数值,因此0.3变为0,0.5变为1。

我可以做些什么来让DOM以指定的速度精确移动?我无法使用任何库。

编辑:我更深入地研究了这个问题,我相信它是elem.offsetTop将数字四舍五入为整数。

但是,我发现CSS OM规范将offsetTop的类型更改为offsetTopthe Chromium team was working on applying the change on the browser more than 4 years ago, and it seems that it should be fixed by now

为什么它不能用于我的程序,我怎样才能使它工作?

EDIT2:我从CSSOM working draft发现float的类型是整数。

offsetTop

我认为他们只将readonly attribute long offsetTop; scrollTop的类型更改为双精度数。

scrollLeft

1 个答案:

答案 0 :(得分:2)

HTMLElement.offset[Left | Top]返回long类型值(即整数)。

如果您想要浮动值,请使用Element.getBoundingClientRect

var requestFrameAnimationId;

    function myMove(offset) {
      var elem = document.getElementById("animate");
      requestFrameAnimationId = animationLoop(frame);

      function frame() {
        // build up our own high precision offsetTop
        var parentRect = elem.offsetParent && elem.offsetParent.getBoundingClientRect() || {top: 0, left:0};
      	var elemRect = elem.getBoundingClientRect();
        var rect = {
          top: elemRect.top - parentRect.top,
          left: elemRect.left - parentRect.left
        };
        if (rect.top >= 350) {
          cancelAnimationFrame(requestFrameAnimationId);
        } else {
          // so we can substract it here
          elem.style.top = (rect.top + offset) + 'px';
          elem.style.left = (rect.left + offset) + 'px';
        }
      }
    }

    function animationLoop(render) {
      var running, lastFrame = +new Date(); // casting Date to Number
      function loop(now) {
        requestFrameAnimationId = requestAnimationFrame(loop);
        running = render(now - lastFrame);
        lastFrame = now;
      }
      loop(lastFrame);
    }
#container {
  width: 400px;
  height: 400px;
  position: relative;
  background: yellow;
}

#animate {
  width: 50px;
  height: 50px;
  position: absolute;
  background-color: red;
}
<p>
  <button onclick="myMove(0.3)">Move at 0.3px per frame</button>
  <button onclick="myMove(0.5)">Move at 0.5px per frame</button>
</p>

<div id="container">
  <div id="animate"></div>
</div>

或者只是将您的值加到变量中:

var requestFrameAnimationId;

function myMove(offset) {
  var elem = document.getElementById("animate");
  requestFrameAnimationId = animationLoop(frame);
  var pos = 0;

  function frame() {
    pos += offset;
    if (pos >= 350) {
      cancelAnimationFrame(requestFrameAnimationId);
    } else {
      elem.style.top = pos + 'px';
      elem.style.left = pos + 'px';
    }
  }
}

function animationLoop(render) {
  var running, lastFrame = +new Date(); // casting Date to Number
  function loop(now) {
    requestFrameAnimationId = requestAnimationFrame(loop);
    running = render(now - lastFrame);
    lastFrame = now;
  }
  loop(lastFrame);
}
#container {
  width: 400px;
  height: 400px;
  position: relative;
  background: yellow;
}

#animate {
  width: 50px;
  height: 50px;
  position: absolute;
  background-color: red;
}
<p>
  <button onclick="myMove(0.3)">Move at 0.3px per frame</button>
  <button onclick="myMove(0.5)">Move at 0.5px per frame</button>
</p>

<div id="container">
  <div id="animate"></div>
</div>