2016-03-19 16 views
2

Orada! sorunsuz hareket eden #drag eleman almak için bir sorunum var. Hareketli eleman oldu mousemove olayla sorun mousemove olay çok fazlaMousemove olayıyla birlikte Javascript hareket öğesi 60 FPS requestAnimationFrame

yüzden, ben onların yöntemi kullanmış deneyin ateş": o söyledi http://www.html5rocks.com/en/tutorials/speed/animations/#debouncing-mouse-events

:

ben bu yazıda bakmak kullanarak requestAnimationFrame + boolean checking canlı eylem için bu keman de

görünüm:. 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); 

bu onu, başarılı bir doğru yolu nedir?

Kodumdaki sorun nedir?

sayesinde

+0

Evet, kodunuzdaki sorun nedir? – Kaiido

+0

Yorum yapmak için Kaiido'ya teşekkürler. Gerçekten mi? hissediyorum, gerçekten pürüzsüz değil? –

+0

FF'm mükemmel bir şekilde pürüzsüz, biraz kromda yanıp sönüyor, bu doğru, ama kesinlikle bu kırmızı renk yüzünden. – Kaiido

cevap

4

sorun mouseDown olay dinleyicisi içinde requestAnimationFrame() kullanıyor olmasıdır. Tüm tıklamaları, mouseMove olay dinleyicisinde yapmalısınız çünkü fare tıklatıldığında fare hareket etmediğinde ekranınızı güncellemek istersiniz. Buna göre, update işlevindeki tüm değişkenlerinizi isMouseDown koşulunda güncelleştirmelisiniz. Kodu aşağıdaki gibi düzeltmeyi öneririm.

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); 

o kadar kontrol 10

+0

MouseMove() ayrıca buna çağırırsa, neden animasyon dizisini upadte() içinde tekrarlamanızı istersiniz? Çerçeve başına birden fazla mouseMove olayı varsa, önceki isteğin iptal edilmesi gerekmez mi? –

+0

@Ilpo Oksanen Evet, haklı görünüyorsunuz. Bu özel durumda, requestAnimationFrame öğesinin sözde özyinelemeli beslemesi gereksizdir. Ekranı güncellemeye yönelik standart yaklaşımımdaki mayın artığına dair bir bakış. İşaret ettiğin için çok teşekkür ederim. Buna göre düzelteceğim. – Redu

+2

Hala hepsi aynı anda idam edilecek requestAnimationFrame için çok fazla çağrı istifliyorsunuz. Bu çerçevede bir olayı zaten ele alıp almadığınızı kontrol etmek, rAF'yi çağırmadan önce yükseltmek ve rAF geri aramada yanlış olarak ayarlamak için bir boolean kullanın. Zaten true olarak ayarlanmışsa, rAF'yi arama. – Kaiido

1

Sizin kodunuz zaten düzgün çalışıyor olmalıdır. Ancak, burada yapmak başka bir yolu:

Sen emin olmalıyız yalnızca bir requestAnimationFrame çağrı kare başına geçelim gecikme ve azalma neden olabilir, aksi update() sonraki repaint anda birden fazla kez adı verilecek, senin fps. Bunu yapmak için, istediğiniz çerçeveyi ve zaten bir çerçeve dizilmişse her bir mousemove olay kontrolünü kaydetmek istersiniz. Varsa, iptal etmek ve yeni bir istek yapmak için cancelAnimationFrame'u kullanmak isteyeceksiniz. Bu şekilde, update() sadece tarayıcı değişiklikleri yapabildiği kadar sık ​​çağrılır (çoğu tarayıcıda I.E. 60 fps).

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! 
} 

lastUpdateCallnull değilse de sadece tekrar requestAnimationFrame arayamadım, ama bu her seferinde animasyon çerçeve çağrısı dışında event yangınları mesafeyi hesaplamak olurdu anlamına geleceğini, veya animasyon gecikme olur farenizin arkasında 20 ms'ye kadar. Her iki yöntem de, sanırım.

İlgili konular