2012-01-04 1078 views
7

Tôi đang sử dụng ràng buộc/unbind cho mousewheel di chuyển, dựa trên SO phản ứng này:jQuery cụ cuộn bằng con lăn chuột - di chuyển ONE vị trí và ngăn chặn

Jquery, unbinding mousewheel event, then rebinding it after actions are completed?

Tôi đang đào hầm lên cây sự kiện từ đồng bằng, để chỉ nhắm mục tiêu các giá trị con lăn X. Tất cả đều hoạt động tốt. Vấn đề tôi đang cố gắng vượt qua: Tôi muốn chỉ đơn giản là di chuyển về phía trước/sau một bảng điều khiển, sau đó ngừng cuộn. Hiện tại, tôi đang hủy liên kết sự kiện con lăn chuột ngay lập tức sau khi di chuyển và điều đó có hiệu quả ngăn việc di chuyển ... nhưng việc hủy liên kết sự kiện con lăn chuột cũng sẽ kích hoạt trang. Những gì tôi cần là để có thể sniff giá trị deltaX đầu tiên cho hướng, sau đó thực hiện một di chuyển và ngừng lắng nghe. Tôi có cần phải tìm đến autoscroll cho câu trả lời? Binding/unbinding cảm thấy klugy, nhưng tôi không thể, cho cuộc sống của tôi, con số làm thế nào để kick ra sau khi một di chuyển, trong khi vẫn sau đó có thể di chuyển sau khi di chuyển đó là hoàn tất. Đây là chức năng mousewheel tôi:

function mouseHandler(event, delta) { 
$('.homeScrollable').bind('mousewheel', mouseHandler); 

var deltaX = event.originalEvent.wheelDeltaX; 
//console.log(event.originalEvent.wheelDeltaX); 

if(deltaX <= 0) { 
    //move forward 1 screen and stop 
    scrollapi.move(1); 
    $('.homeScrollable').unbind('mousewheel', mouseHandler); 
} else if(deltaX > 0) { 
    //move backward 1 screen and stop 
    scrollapi.move(-1); 
    $('.homeScrollable').unbind('mousewheel', mouseHandler); 
} 
event.preventDefault(); 

// Rebind mousewheel event: 
$('.homeScrollable').bind('mousewheel', mouseHandler);  }; 

Tôi cũng đã xem xét thiết lập một bộ đếm thời gian, a la:

jquery mousewheel plugin: how to fire only one function every scroll

mà dường như vô cùng hứa hẹn, nhưng không di chuyển. Đây là trang plugin cho anh chàng này: http://brandonaaron.net/code/mousewheel/docs

Cảm ơn bạn đã kiểm tra.

Trả lời

18

Vì DOM không cung cấp bất kỳ cách nào khác biệt giữa sự kiện cuộn đầu tiên được kích hoạt và các sự kiện tiếp theo xảy ra là một phần của cùng một chuyển động cuộn, chúng tôi buộc phải suy nghĩ về các phương pháp gián tiếp phân biệt giữa chúng.

Nếu bạn nhanh chóng cuộn qua bất kỳ phần tử cụ thể nào, sự kiện cuộn được kích hoạt nhiều lần theo trình tự. Sử dụng đoạn mã sau, chúng ta có thể có được một ý tưởng về chính xác mức độ thường xuyên xảy ra:

$('#exampleDiv').bind('mousewheel', function() { 
    console.log(new Date().getTime()); 
}); 

Khi bạn di chuyển qua div đó, bạn sẽ nhận được một đầu ra giao diện điều khiển trông giống như thế này:

// Using mouse wheelbar 
251327626600149 
251327626600215 
251327626600265 
251327626600282 
251327626600332 
251327626600365 

// Using touchpad 
251327626626207 
251327626626225 
251327626626261 
251327626626276 
251327626626312 
251327626626345 

Nhìn vào đầu ra này, có vẻ như các sự kiện mousescroll thường được kích hoạt trong khoảng giữa 20 ms và 60 ms của nhau. Để an toàn, chúng tôi sẽ mất phần trên cùng là 100 mili giây. Điều này rất thông tin, bởi vì chúng ta có thể sử dụng nó để phân biệt giữa các sự kiện cuộn là một phần của cùng một hành động và các sự kiện có khả năng khác biệt và được chủ ý bắt đầu bởi người dùng.

Điều bạn có thể làm từ đây là tạo biến 'dấu thời gian' có thể truy cập toàn cầu, cập nhật nó mỗi lần sự kiện mousescroll được kích hoạt, cho dù thành công hay không. Một cái gì đó như thế này:

var timeStamp = new Date().getTime(); 

$('#exampleDiv').bind('mousewheel', function (event) { 
    var timeNow = new Date().getTime(); 

    // Need to prevent the default scrolling behavior 
    event.preventDefault(); 

    // If the last mousescroll happened less that 100 ms ago, update 
    // timeStamp and do nothing 
    if (timeNow - timeStamp < 100) { 
     timeStamp = timeNow; 
     return; 
    } else { 
     timeStamp = timeNow; 
     scrollToSomeOtherDiv(); 
    } 
}); 

này có hiệu quả bỏ qua tất cả mousescroll sự kiện được bắn sau sự kiện ban đầu mà trước tất cả nhưng bắt đầu hoạt động trở lại sau khi người dùng đã dừng lại 100 ms.

Điều này sẽ giải quyết được vấn đề của bạn, ngoại trừ nếu chức năng scrollToSomeOtherDiv() của bạn có liên quan đến một số loại hoạt ảnh tốn nhiều thời gian. Tất nhiên, bạn có thể tạo ra một boolean isAnimating boolean toàn cục và kiểm tra xem nó có đúng hay không mỗi khi một sự kiện mousescroll được kích hoạt (đảm bảo rằng nó bị sai khi gọi lại khi hoạt ảnh kết thúc).

Điều đó có hiệu quả, ngoại trừ việc nó có thể mang lại trải nghiệm chói tai cho người dùng. Một người muốn cuộn qua hai bảng nhanh chóng có khả năng sẽ không tạm dừng giữa các cuộn ngay cả sau khi nhìn thấy hoạt ảnh bắt đầu. Mã ở trên sẽ thấy tất cả các sự kiện mousescroll của chúng như là một phần của cùng một chuyển động cuộn và tiếp tục bỏ qua chúng!

Trong trường hợp đó, bạn có thể chỉ cần sử dụng thời gian hoạt ảnh làm ngưỡng của mình. Bạn đặt một timeStamp khi hoạt ảnh được bắt đầu và sau đó bỏ qua tất cả các sự kiện mousescroll trong khoảng thời gian đó. Tôi đã viết lên một ví dụ ở đây: http://jsfiddle.net/Sg8JQ/

Mã liên quan là ở đây:

var lastAnimation = 0; 
var animationTime = 1000; // in ms 
var quietPeriod = 500; // in ms, time after animation to ignore mousescroll 

function scrollThis(event, delta, deltaX, deltaY) { 
    var timeNow = new Date().getTime(); 

    // change this to deltaX/deltaY depending on which 
    // scrolling dir you want to capture 
    deltaOfInterest = deltaY; 

    if (deltaOfInterest == 0) { 
     // Uncomment if you want to use deltaX 
     // event.preventDefault(); 
     return; 
    } 

    // Cancel scroll if currently animating or within quiet period 
    if(timeNow - lastAnimation < quietPeriod + animationTime) { 
     event.preventDefault(); 
     return; 
    } 

    if (deltaOfInterest < 0) { 
     if ($('.active').next('div').length) { 
      lastAnimation = timeNow; 
      $('.active').removeClass('active').next('div').addClass('active'); 
      $('html,body').animate({ 
       scrollTop: $('.active').offset().top }, animationTime); 
     } 
    } else { 
     if ($('.active').prev('div').length) { 
      lastAnimation = timeNow; 
      $('.active').removeClass('active').prev('div').addClass('active'); 
      $('html,body').animate({ 
       scrollTop: $('.active').offset().top }, animationTime); 
     } 
    } 
} 

// Note: mousewheel() is defined in the mousewheel plugin by Brandon Aaron 
// You could do without it, but you'd need to adjust for firefox and webkit 
// separately. 
// 
// You couldn't use $(document).scroll() because it doesn't allow you to 
// preventDefault(), which I use here. 
$(document).mousewheel(scrollThis); 

tôi cũng đã bao gồm quietPeriod đó là thời gian ngoài thời gian hoạt hình trong thời gian đó bạn muốn tiếp tục làm ngơ mousescroll sự kiện . Bạn có thể đặt thành 0 nếu bạn muốn cuộn được 'đáp ứng' ngay khi hoạt ảnh hoàn tất.

+0

Mọi giải pháp mà tôi nghĩ ra Đối với dự án được đề cập, các mod thiết kế cuối cùng đã phủ nhận vấn đề này, nhưng nó vẫn khiến tôi phải lo lắng. te() trên cửa sổ thay đổi kích cỡ. Nó đặt tâm trí của tôi trở lại về vấn đề này. Tôi tin rằng những gì bạn đã cung cấp là điều quyến rũ nhất/đáng yêu nhất mà tôi đã gặp cho đến nay. Vì điều đó, tôi đánh dấu bạn là người chiến thắng. – wlangley

+1

Tuyệt vời! Gọi một đoạn mã 'gợi cảm' là về nhiều lời khen như tôi có thể xử lý. Tôi đã suy nghĩ về ý tưởng thiết kế ban đầu của bạn mặc dù - với cuộn ngang, bạn vẫn gặp phải một số vấn đề với cử chỉ trên mac (vì hai ngón tay vuốt cả cuộn và đưa bạn trở lại lịch sử trên Chrome và Safari). Tôi chưa tìm ra giải pháp cho vấn đề _that_. – pikappa

+0

Với các giá trị dưới 500 (nói về animationTime và quietPeriod), mẫu này ngừng hoạt động. thử lại jsfiddle của bạn với giá trị hạ xuống và bạn sẽ thấy điểm mà trong tâm trí của tôi, nó ngừng hoạt động như mong đợi. Giải pháp được đánh giá cao nếu có bất kỳ – Ben

0

Hãy xem điều này và xem suy nghĩ của bạn. Để bắt đầu, tôi sẽ không liên kết với mousewheel vì Firefox sử dụng DOMMouseScroll để thay thế. Điều này cũng sử dụng một trình xử lý delta khác, đó là nghịch đảo của trình xử lý được tất cả các trình duyệt khác sử dụng. Thay vào đó liên kết với sự kiện cuộn của jQuery để bình thường hóa hành vi. Bạn có thể theo dõi vị trí cuộn cuối cùng để xác định xem người dùng có cuộn lên hoặc xuống hay không.

Tôi là loại giả định rằng bạn đang làm động giữa các phần. Có chức năng gọi lại nào mà bạn có thể sử dụng để xác định xem bạn có nên cuộn không? Tôi đã tạo ra toàn cầu để theo dõi liệu các phần tử hiện có đang hoạt ảnh hay không. Nếu vậy thì chúng ta không bận tâm thực thi hàm. Cuối cùng, tôi nhận thấy rằng gọi lại cho hoạt ảnh cuộn dường như kích hoạt trước khi sự kiện cuộn cuối cùng xảy ra, vì vậy tôi thực sự phải sử dụng một setTimeout ở đó. Tôi không thích nó, nhưng tôi không thể tìm ra cách khác để gọi lại để làm việc đúng cách.

http://jsfiddle.net/Gj3Qy/

var lastScrollTop = 0; 
var isDoingStuff = false; 

$(document).scroll(function(event) { 
    //abandon 
    if(isDoingStuff) { return; } 

    if ($(this).scrollTop() > lastScrollTop) { 
     //console.log('down'); 
     $('.active').removeClass('active').next('div').addClass('active'); 
     isDoingStuff = true; 
     $('html,body').animate({scrollTop: $('.active').offset().top }, 1000, function() { 
      setTimeout(function() {isDoingStuff = false;}, 100); 
      console.log('off'); 
     }); 
    } else { 
     //console.log('up'); 
    } 
    lastScrollTop = $(this).scrollTop(); 
}) 
+0

giả định của bạn là đúng - Tôi có một 1 trang -page, và đang cuộn giữa các phần - nhưng trang/di chuyển ngang. Vì vậy, tôi sẽ cần phải sử dụng scrollLeft thay vì scrollTop, tôi đoán. Nhưng vấn đề lớn hơn là có một thuộc tính của đối tượng Scrollable Tools được gọi là 'mousewheel' (http://flowplayer.org/tools/demos/scrollable/plugins/index.html), và nếu tôi không sử dụng nó (set để đúng hoặc liên kết đến cuối), cuộn bánh xe chuột nukes trang web. Có vẻ như có một cách để trì hoãn hành động (scrollable.move()) cho đến khi deltaX ngừng thay đổi, sau đó di chuyển một lần. – wlangley

+0

Tôi tự hỏi nếu .throttle() có thể là viên đạn ma thuật của tôi. http://benalman.com/code/projects/jquery-throttle-debounce/examples/throttle/ – wlangley

+0

Hmm, tôi bị kẹt. Không chắc chắn làm thế nào bạn sẽ giải quyết điều này = (. – mrtsherman

0

Vì vậy, đây là những gì tôi đang làm. Nó có vẻ hoạt động nhưng vẫn còn lỗi.

i=0; 
$("#test").mousewheel(function(event, delta) { 
    event.preventDefault(); 
    clearTimeout($.data(this, 'timer'));   

    // check if the mouse moved on right/up 
    if(delta > 0) {    
     i++;     

     // hack to execute function just once 
     if(i == 3) {     
      $('#child').fadeIn().html("<h1>next</h1>").fadeOut(); // do something once when i=4, but happening multiple times 
     }   

    } else { // check if mouse moved left/down 

     i--;     

     if(i==-3) {  
      $('#child').fadeIn().html("<h1>previous</h1>").fadeOut(); // do something once when i=-4, but happening multiple times 
     }    

    } // end if delta 

    // only execute another mousewheel scroll after some delay 
    $.data(this, 'timer', setTimeout(function() { 
     i = 0; 
    }, 100)); 

}); 

Vì vậy, trong khi tăng i in ra tốt, nếu tôi bỏ ghi chú nếu i = 4 dòng in, nó không hoạt động đúng cách (bạn có thể phải di chuyển khó khăn nếu bạn đang sử dụng một con chuột, im cố gắng trong trackpad của tôi). Trong khi tôi hiểu nó khi tôi liên tục tăng khi bạn nhận được giá trị delta, bạn chỉ thực hiện điều gì đó khi tôi đạt đến 4 và chỉ đặt lại giá trị của nó nếu cuộn bị tạm dừng 250ms, mã này dường như đặt lại i thành 0 ít nhất hai lần hoặc ba lần == 4 thực sự thực hiện hai lần hoặc ba lần. Có lẽ bạn có thể tìm thấy những gì có thể xảy ra. Tôi gần như ở đó, chỉ cần sửa lỗi đó.

Hãy thử thêm một cái gì đó thay đổi như fadeIn, fadeOut có hiệu lực trong đó nếu (i == 4) tuyên bố và nổi bật hơn của nó.

EDIT:

Heres the new edited version.Liên kết mousewheel đã cũ nên có lẽ điều này cho thấy nó tốt hơn bây giờ. Nếu bạn di chuyển nhanh chóng, bạn thấy nó nhấp nháy ...

+0

Đây có phải là câu trả lời cho OP hay bạn đang đăng câu hỏi của riêng mình không? Đây không phải là một diễn đàn thảo luận – Sparky

+0

Đây là một câu trả lời: – Chandra

0

Tôi nghĩ giải pháp được cung cấp bởi "pikappa" là sự gợi cảm thuần khiết.

Đối với tôi, nó hoạt động hoàn hảo trên firefox nhưng tôi đã trải qua "nhấp nháy" trong khi cuộn dưới Chrome 26.0.x

Một đơn giản bẩn "vá" mã tuyệt vời của mình là thêm "return false;" tại dòng 57 và 64 của JSfiddle mình