there! i have a problem for getting #drag
element moving smoothly.
i look at this article : http://www.html5rocks.com/en/tutorials/speed/animations/#debouncing-mouse-events
it said that : "the problem with mousemove
event when moving element was mousemove event fired too much
so, i try to used their method : using requestAnimationFrame
+ boolean checking
.
look at this fiddle for live action : 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);
is this the correct way to accompolished it?
what's wrong with my code?
thanks
Your code should already work fine. However, here's another way to do it:
You need to make sure you only let one requestAnimationFrame
call go through per frame, otherwise update()
will be called multiple times at the next repaint
, which can cause lag and decrease your fps. To do this, you want to save the requested frame and on each mousemove
event check if there's already a frame lined up. If there is, you'll want to use cancelAnimationFrame
to cancel it and make a new request. This way update()
is only called as often as the browser is able to render changes (I.E. 60fps in most browsers).
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!
}
You could also just not call requestAnimationFrame
again if lastUpdateCall
isn't null
, but that would mean you'd have to calculate the distance outside the animation frame call every time the event
fires, or the animation would lag behind your mouse by up to 20ms. Either method is fine, I suppose.
The problem is that you are using requestAnimationFrame()
in the mouseDown
event listener. You should do all you updates in the mouseMove
event listener because you want to update your display when the mouse moves not when mouse clicks. Accordingly you should update all your variables under the isMouseDown
conditional in the update
function. i would suggest correcting the code as follows.
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);
check it up here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With