Javascript移动元素与mousemove事件60 FPS requestAnimationFrame

时间:2016-03-19 04:58:51

标签: javascript html css

有!我有一个让#drag元素顺利移动的问题。

我看这篇文章:http://www.html5rocks.com/en/tutorials/speed/animations/#debouncing-mouse-events

它说:“移动元素时mousemove事件的问题是 mousemove事件被触发太多

所以,我尝试使用他们的方法:使用requestAnimationFrame + boolean checking

看看这个实际行动的小提琴:https://jsfiddle.net/5f181w9t/

HTML:

<div id="drag">this is draggable</div>

CSS:

#drag {width:100px; height:50px; background-color:red; transform:translate3d(0, 0, 0); }

JS:

var el               = document.getElementById("drag"),
    startPosition    = 0, // start position mousedown event
    currentPosition  = 0, // count current translateX value
    distancePosition = 0, // count distance between "down" & "move" event
    isMouseDown      = false; // check if mouse is down or not 

function mouseDown(e) {
    e.preventDefault(); // reset default behavior
    isMouseDown     = true;
    startPosition   = e.pageX; // get position X
    currentPosition = getTranslateX(); // get current translateX value
    requestAnimationFrame(update); // request 60fps animation
}    

function mouseMove(e) {
    e.preventDefault();
    distancePosition = (e.pageX - startPosition) + currentPosition; // count it!  
}

function mouseUp(e) {
    e.preventDefault();
    isMouseDown = false; // reset mouse is down boolean
}

function getTranslateX() {
   var translateX = parseInt(getComputedStyle(el, null).getPropertyValue("transform").split(",")[4]);

   return translateX; // get translateX value

}

function update() {
    if (isMouseDown) { // check if mouse is down
        requestAnimationFrame(update); // request 60 fps animation
    }
    el.style.transform = "translate3d(" + distancePosition + "px, 0, 0)";
  // move it!
}

el.addEventListener("mousedown", mouseDown);
document.addEventListener("mousemove", mouseMove);
document.addEventListener("mouseup", mouseUp);

这是正确的方法吗?

我的代码出了什么问题?

感谢

2 个答案:

答案 0 :(得分:5)

问题是您在requestAnimationFrame()事件侦听器中使用mouseDown。您应该在mouseMove事件侦听器中执行所有更新,因为您希望在鼠标单击时鼠标移动时更新显示。因此,您应该在isMouseDown函数的update条件下更新所有变量。我建议修改代码如下。

<强> HTML

<div id="drag">this is draggable</div>

<强> CSS

#drag {
width:100px;
height:50px;
background-color:red;
transform:translateX(0);
}

<强> JS

var el               = drag,
    startPosition    = 0, // start position mousedown event
    currentPosition  = 0, // count current translateX value
    distancePosition = 0, // count distance between "down" & "move" event
    isMouseDown      = false, // check if mouse is down or not
    needForRAF       = true;  // to prevent redundant rAF calls

function mouseDown(e) {
  e.preventDefault(); // reset default behavior
  isMouseDown     = true;
  currentPosition = getTranslateX(); // get current translateX value
  startPosition   = e.clientX; // get position X
}    

function mouseMove(e) {
    e.preventDefault();
  distancePosition = (e.clientX - startPosition) + currentPosition; // count it!  
  if (needForRAF && isMouseDown) {
    needForRAF = false;            // no need to call rAF up until next frame
    requestAnimationFrame(update); // request 60fps animation
  }; 
}

function mouseUp(e) {
  e.preventDefault();
  isMouseDown = false; // reset mouse is down boolean
}

function getTranslateX() {
  var translateX = parseInt(getComputedStyle(el, null).getPropertyValue("transform").split(",")[4]);
  return translateX; // get translateX value
}

function update() {
  needForRAF = true; // rAF now consumes the movement instruction so a new one can come
  el.style.transform = "translateX(" + distancePosition + "px)";// move it!
}

el.addEventListener("mousedown", mouseDown);
document.addEventListener("mousemove", mouseMove);
document.addEventListener("mouseup", mouseUp);

检查here

答案 1 :(得分:2)

您的代码应该已经正常工作了。但是,这是另一种方法:

您需要确保每帧只允许一个requestAnimationFrame来电,否则update()会在下一个repaint多次调用,这会导致延迟和减少你的fps。要执行此操作,您需要保存请求的帧,并在每个mousemove事件检查中是否已经排成一行。如果有,您将需要使用cancelAnimationFrame取消它并发出新请求。这种方式update()只能在浏览器能够呈现更改时调用(大多数浏览器中为I.E.60fps)。

function mouseDown(e) {
    e.preventDefault(); // cancel default behavior
    isMouseDown     = true;
    startPosition   = e.pageX; // get position X
    currentPosition = getTranslateX(); // get current translateX value
}

var lastUpdateCall=null;
function mouseMove(e){
    if(isMouseDown){ //check if mousedown here, so there aren't any unnecessary animation frame requests when the user isn't dragging
        e.preventDefault(); // You probably only want to preventDefault when the user is actually dragging

        if(lastUpdateCall) cancelAnimationFrame(lastUpdateCall); //if an animation frame was already requested after last repaint, cancel it in favour of the newer event

        lastUpdateCall=requestAnimationFrame(function(){ //save the requested frame so we can check next time if one was already requested
            distancePosition = (e.clientX - startPosition) + currentPosition; // Do the distance calculation inside the animation frame request also, so the browser doesn't have to do it more often than necessary 
            update(); //all the function that handles the request
            lastUpdateCall=null; // Since this frame didn't get cancelled, the lastUpdateCall should be reset so new frames can be called. 
        });
    }
}

function update(){
    el.style.transform = "translateX(" + distancePosition + "px)";// move it!
}

如果requestAnimationFrame不是lastUpdateCall,您也可以不再调用null,但这意味着您每次必须计算动画帧调用之外的距离event点火,或动画会落后于鼠标20毫秒。我想这两种方法都没问题。