有!我有一个让#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);
这是正确的方法吗?
我的代码出了什么问题?
感谢
答案 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毫秒。我想这两种方法都没问题。