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.
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
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
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