остановка события mousewheel дважды в OSX

Я заметил, что событие mousewheel происходит несколько раз в mac osx. Может быть atributed к характеристика инерции.

есть ли способ исправить это поведение?

(самоподписанный ssl не беспокойтесь, пожалуйста!) https://sandbox.idev.ge/roomshotel/html5_v2/

Я использую scrollSections.js https://github.com/guins/jQuery.scrollSections

и он использует плагин mousewheel jquery: https://github.com/brandonaaron/jquery-mousewheel

Я вижу много людей, имеющих ту же проблему:https://github.com/brandonaaron/jquery-mousewheel/issues/36

есть некоторые решения, но никто не работает с плагином scrollSections.

есть идеи, как отключить эту функцию инерции от JS?

моя попытка исправить:

// Fix for OSX inertia problem, jumping sections issue.
if (isMac) {

  var fireEvent;
  var newDelta = deltaY;

  if (oldDelta != null) {

    //check to see if they differ directions
    if (oldDelta < 0 && newDelta > 0) {
      fireEvent = true;
    }

    //check to see if they differ directions
    if (oldDelta > 0 && newDelta < 0) {
      fireEvent = true;
    }

    //check to see if they are the same direction
    if (oldDelta > 0 && newDelta > 0) {

      //check to see if the new is higher
      if (oldDelta < newDelta) {
        fireEvent = true;
      } else {
        fireEvent = false;
      }
    }

    //check to see if they are the same direction
    if (oldDelta < 0 && newDelta < 0) {

      //check to see if the new is lower
      if (oldDelta > newDelta) {
        fireEvent = true;
      } else {
        fireEvent = false;
      }
    }

  } else {

    fireEvent = true;

  }

  oldDelta = newDelta;

} else {

  fireEvent = true;

}

вы можете увидеть исправление, реализованное здесь: https://sandbox.idev.ge/roomshotel/html5_v2/ но это хит / промах.

4 ответов


последнее решение с тайм-аутами имело один главный недостаток: кинетический эффект прокрутки мог длиться довольно долго (даже 1s или около того)... и отключение прокрутки в течение 1-2 секунд не было бы лучшим решением.

Soooo, как и было обещано, вот еще один подход.

наша цель-обеспечить один ответ один действие пользователя, которое в данном случае является прокруткой.

что это 'очень'? ради решения этой проблема, скажем так 'One scrolling' - это событие, которое длится с момента начала перемещения страницы до момента окончания движения.

кинетический эффект прокрутки достигается путем перемещения страницы много раз (скажем, каждые 20 МС) на небольшое расстояние. Это означает, что наша кинетическая прокрутка состоит из многих-многих маленьких линейных "прокруток".

эмпирическое тестирование показало, что эти маленькие "прокрутки" происходят каждые 17-18 мс в середине кинетического прокрутка, и около 80-90ms в начале и конце. Вот простой тест, который мы можем настроить, чтобы увидеть это:

var oldD;
var f = function(){
    var d = new Date().getTime();
    if(typeof oldD !== 'undefined')
        console.log(d-oldD);
    oldD = d;
}
window.onscroll=f;

важно! Каждый раз, когда происходит эта мини-прокрутка, запускается событие прокрутки. так:

 window.onscroll = function(){console.log("i'm scrolling!")};

будет уволен 15 до 20 + раз во время одного кинетического свитка. Кстати, onscroll имеет действительно хорошую поддержку браузера (см. таблица совместимости), поэтому мы можем положиться на него (за исключением сенсорных устройств, я немного покрою эту проблему позже);

некоторые могут сказать, что окна переосмысление.onscroll-не лучший способ установить прослушиватели событий. Да, вам рекомендуется использовать

 $(window).on('scroll',function(){...});

или что угодно, это не суть проблемы (я лично использую свою собственную библиотеку).

Итак, с помощью события onscroll мы можем достоверно сказать, принадлежит ли это конкретное мини-движение страницы к одному длительному кинетическому свитку, или это новый:

    var prevTime = new Date().getTime();
    var f = function(){
        var curTime = new Date().getTime();
        if(typeof prevTime !== 'undefined'){
            var timeDiff = curTime-prevTime;
            if(timeDiff>200)
                console.log('New kinetic scroll has started!');
        }
        prevTime = curTime;
    }
    window.onscroll=f;

вместо "успокаивать.log " вы можете вызвать желаемую функцию обратного вызова (или обработчик событий), и все готово! Функция будет срабатывать только один раз на каждом кинетическом или простом свитке, что и было нашей целью.

возможно, вы заметили, что я использовал 200 мс в качестве критерия того, является ли это новым свитком или частью предыдущего свитка. Это до вас, чтобы установить его на большие значения, чтобы быть 999% уверены, что вы предотвратить любые дополнительные вызовы. Однако, пожалуйста, имейте в виду, что это не что мы использовали в моей предыдущий ответ. Это просто период времени между любыми двумя движениями страницы (будь то новый свиток или небольшая часть кинетического свитка). На мой взгляд, очень мало шансов, что между шагами в кинетическом свитке будет отставание более 200 мс (иначе оно вообще не будет гладким).

как я уже упоминал выше, событие onscroll работает по-разному на сенсорных устройствах. Он не будет стрелять во время каждого маленького шага кинетического свитка. Но он выстрелит, когда движение страницы наконец-то закончилось. Кроме того, есть событие ontouchmove... Так что ничего страшного. При необходимости я могу предоставить решение и для сенсорных устройств.

P. S. Я понимаю, что я написал слишком много, поэтому я буду рад ответить на все ваши вопросы и предоставить дополнительную код если вам нужен.

предоставленное решение поддерживается во всех браузерах, очень легкое и, что более важно, подходит не только для Mac, но и для каждого устройства, которое может реализовать кинетическая прокрутка, поэтому я думаю,что это действительно путь.


вы знаете, я думаю, что в этом случае лучше использовать таймауты. Почему бы не написать что-нибудь вроде этого:

// Let's say it's a global context or whatever...:
var fireEvent = true;
var newDelta, oldDelta, eventTimeout;
newDelta = oldDelta = eventTimeout = null;

// ... and the function below fires onmousewheel or anything similar:
function someFunc(){
    if(!fireEvent) return; // if fireEvent is not allowed => stop execution here ('return' keyword stops execution of the function), else, execute code below:
    newDelta = deltaY;
    if(oldDelta!=null&&oldDelta*newDelta>0){ // (1.1) if it's not the first event and directions are the same => prevent possible dublicates for further 50ms:
        fireEvent = false;
        clearTimeout(eventTimeout); // clear previous timeouts. Important!
        eventTimeout = setTimeout(function(){fireEvent = true},500);
    }
    oldDelta = newDelta;
    someEventCallback(); // (1.2) fire further functions...
    }

таким образом, любое событие mousewheel, запущенное в течение полсекунды после любого предыдущего вызова события mousewheel, будет проигнорировано, если оно сделано в том же направлении, что и предыдущее (см. условие в 1.1). это решит проблему, и пользователь никоим образом не обнаружит это. Сумма задержки может быть изменена, чтобы лучше удовлетворить ваши потребности.

решение изготовлено на чистом JS. Вы можете задать любые вопросы об интеграции в вашу среду, но затем мне нужно, чтобы вы предоставили дополнительный код своей страницы.

P. S. Я не видел ничего похожего на eventCallback() позвонить в ваш код (см. раздел 1.2 о моем решении). был только флаг fireEvent. Вы делали что-то вроде:

if(fireEvent)
    someEventCallback();

позже или что-то?

P. P. S. Обратите внимание, что fireEvent должен быть в глобальной области, чтобы работать здесь с setTimeout. Если это нет, также довольно легко заставить его работать нормально, но код нужно немного изменить. Если это ваше дело, скажите мне, и я все улажу.

обновление

после короткого поиска я узнал, что аналогичный механизм используется в функции _debounce() подчеркивания. См. раздел документация подчеркивания здесь


У вас есть, хотя об использовании fullpage.js? Он имеет задержку между прибытием в раздел и моментом, когда вы можете прокрутить к следующему разделу, который решает часть проблемы пользователей Mac с трекпадами или Apple magic mouses.

Он также предоставит вам некоторые другие преимущества, такие как гораздо больше опций, методов и совместимости с сенсорными устройствами и старыми браузерами без поддержки CSS3.


чтобы начать с чего-то, давайте сделаем ваше решение короче (поэтому его легче понять и отладить):

var fireEvent;
var newDelta = deltaY;
var oldDelta = null;

fireEvent = EventCheck();
oldDelta = newDelta;

function EventCheck(){
    if(oldDelta==null) return true; //(1.1)
    if(oldDelta*newDelta < 0) return true; // (1.2) if directions differ => fire event
    if(Math.abs(newDelta)<Math.abs(oldDelta)) return true; // (1.3) if oldDelta exceeds newDelta in absolute values => fire event
    return false; // (1.4) else => don't fire;
}

как вы видите, это абсолютно то, что делает ваш код. Однако я не могу понять эту часть вашего кода (которая соответствует (1.3) в моем фрагменте):

 //check to see if the new is lower
 if (oldDelta > newDelta) {
    fireEvent = true;
 } else {
    fireEvent = false;
 }

из кода при условии, что неясно, как рассчитывается deltaY. Как можно предположить, дельта равна endPosition-initialPosition. Итак, oldDelta>newDelta не означает, что новый должность ниже, но новый разрыв между этими двумя значениями больше. Если это то, что он означает, и вы все еще используете его, я полагаю, вы пытаетесь отслеживать инерцию с этим. Затем вы должны изменить оператор comparative (использовать less than вместо greater then и наоборот). Другими словами, Я бы написал:

if(Math.abs(newDelta)>Math.abs(oldDelta)) return true; // (1.3) 

вы видите, теперь я использовал оператор "больше, чем", что означает: newDelta превышает oldDelta в абсолютных значениях => это не инерция, и вы все равно можете запустить событие.

Это то, чего вы пытаетесь достичь, или я неправильно истолковал ваш код? Если да, то объясните, как рассчитывается deltaY и какова была ваша цель, сравнивая старые и новые дельты. P. S. Я бы предложил не использовать если(isMac) на этом этапе, пока проблема также может потенциально скрываться там.