2016-08-14 13 views
21

Mam sytuację, w której na przykład, jeśli przewinięcie użytkownika spowoduje zmianę w tekście przewijania o 1000 pikseli, chciałbym wiedzieć z wyprzedzeniem.Czy można ustalić, w którym miejscu przewijania skończy się przy użyciu javascript? Jeśli tak to jak?

Doskonałym przykładem jest kontrola iCalendar nad przewijaniem użytkownika. Bez względu na stopień przewijania w aplikacji iCalendar, najdalszy możliwy do przewinięcia jest następny lub poprzedni miesiąc.

Obecnie mam bardzo hackowate rozwiązanie ograniczające przewijanie, które uwzględnia tylko to, gdzie aktualnie znajduje się przewinięcie użytkownika.

MyConstructor.prototype._stopScroll = function(){ 

    //Cache the previous scroll position and set a flag that will control 
    //whether or not we stop the scroll 
    var previous = this._container.scrollTop; 
    var flag  = true; 

    //Add an event listener that stops the scroll if the flag is set to true 
    this._container.addEventListener('scroll', function stop(){ 
     if(flag) { 
      this._container.scrollTop = previous; 
     } 
    }.bind(this), false); 

    //Return a function that has access to the stop function and can remove it 
    //as an event listener 
    return function(){ 
     setTimeout(function(){ 
      flag = false; 
      this._container.removeEventListener('scroll', stop, false); 
     }.bind(this), 0); 
    }.bind(this); 
}; 

Podejście to działa i przestaną zwój w toku, ale to nie jest gładka i chciałbym wiedzieć, czy istnieje lepszy sposób, aby tego dokonać.

Kluczem do tego pytania jest to, czy mogę wiedzieć, z wyprzedzeniem, gdzie kończy się zwój. Dzięki!!!

+0

Jak byś wiedział, gdy użytkownik przestanie z wyprzedzeniem? – epascarello

+0

@epascarello Mam na myśli zasadniczo jeden gest. Mimo że nasze zwoje wyglądają na ciągłe, składają się z sekwencji dyskretnych gestów z określonymi wartościami przewijania. Inny sprzęt traktuje te gesty w inny sposób, ale gdzieś na komputerze wie, jak daleko będzie się poruszał zwój. – Robert

+1

Możesz wspomnieć, że chodzi tu o przewijanie bezwładności/pędu, a nie zwykłe przewijanie myszy. Nie wiem, jak to zrobić. –

Odpowiedz

6

Edit: Wystarczy znaleźć następujący projekt na github:

https://github.com/jquery/jquery-mousewheel

Próbowałem demo i jest w stanie zgłosić mój touchpad i mysz prędkość przewijania. Może również zatrzymać przewijanie bez hacków o ustalonej pozycji: D

Zajrzę w kilka następnych dni i zobaczę, czy mogę napisać cokolwiek, co zgłasza prędkość przewijania, kierunek, prędkość, urządzenie itd. Mam nadzieję, że " m jest w stanie stworzyć wtyczkę jQuery, która może zastąpić całą przewijaną interakcję.

Zaktualizuję ten wpis, gdy będę miał więcej informacji na ten temat.


Nie można przewidzieć, gdzie skończy się przewijanie myszy.

Z drugiej strony przesuwanie ekranu dotykowego/touchpada ma pewną prędkość, która zwolni po zatrzymaniu użytkownika, jak samochód, który popchnął i zaczyna zwalniać.

Niestety, każda przeglądarka/os/sterownik/ekran dotykowy/touchpad/etc ma własną implementację dla tej spowalniającej części, więc nie możemy tego przewidzieć.


Ale możemy oczywiście napisać własną implementację.

mamy 3 implementacje, które mogą być wykonane:

A. Kierunek

B. kierunek i prędkość

C, kierunku, prędkości i prędkość


iCalender prawdopodobnie używa implementacji A.


Wykonanie A:

Wyjścia przewijać kierunek pocieszyć, użytkownik jest w stanie przewinąć +/- 1px zanim zostanie wykryty kierunek.

Demo on JSFiddle

Demo with animation on JSFiddle

(function iDirection() { 
    var preventLoop = true; 
    var currentScroll = scrollTop(); 
    function scroll() { 
     if(preventLoop) { 
      //Get new scroll position 
      var newScroll = scrollTop(); 

      //Stop scrolling 
      preventLoop = false; 
      freeze(newScroll); 

      //Check direction 
      if(newScroll > currentScroll) { 
       console.log("scrolling down"); 
       //scroll down animation here 
      } else { 
       console.log("scrolling up"); 
       //scroll up animation here 
      } 
      /* 
      Time in milliseconds the scrolling is disabled, 
      in most cases this is equal to the time the animation takes 
      */ 
      setTimeout(function() { 
       //Update scroll position 
       currentScroll = newScroll; 

       //Enable scrolling 
       unfreeze(); 

       /* 
       Wait 100ms before enabling the direction function again 
       (to prevent a loop from occuring). 
       */ 
       setTimeout(function() { 
        preventLoop = true; 
       }, 100); 
      }, 1000); 
     } 
    } 
    $(window).on("scroll", scroll); 
})(); 


Wykonanie B:

Wyjścia przewiń kierunek, odległość i średnia prędkość pocieszyć, użytkownik jest w stanie aby przewinąć liczbę pikseli ustawionych w zmiennej distance.

Jeśli użytkownik przewinie szybko, mogą przewinąć jeszcze kilka pikseli.

Demo on JSFiddle

(function iDirectionSpeed() { 
    var distance = 50; //pixels to scroll to determine speed 
    var preventLoop = true; 
    var currentScroll = scrollTop(); 
    var currentDate = false; 
    function scroll() { 
     if(preventLoop) { 
      //Set date on scroll 
      if(!currentDate) { 
       currentDate = new Date(); 
      } 

      //Get new scroll position 
      var newScroll = scrollTop(); 

      var scrolledDistance = Math.abs(currentScroll - newScroll); 

      //User scrolled `distance` px or scrolled to the top/bottom 
      if(scrolledDistance >= distance || !newScroll || newScroll == scrollHeight()) { 
       //Stop scrolling 
       preventLoop = false; 
       freeze(newScroll); 

       //Get new date 
       var newDate = new Date(); 

       //Calculate time 
       var time = newDate.getTime() - currentDate.getTime(); 

       //Output speed 
       console.log("average speed: "+scrolledDistance+"px in "+time+"ms"); 

       /* 
       To calculate the animation duration in ms: 
       x: time 
       y: scrolledDistance 
       z: distance you're going to animate 

       animation duration = z/y * x 
       */ 

       //Check direction 
       if(newScroll > currentScroll) { 
        console.log("scrolling down"); 
        //scroll down animation here 
       } else { 
        console.log("scrolling up"); 
        //scroll up animation here 
       } 

       /* 
       Time in milliseconds the scrolling is disabled, 
       in most cases this is equal to the time the animation takes 
       */ 

       setTimeout(function() { 
        //Update scroll position 
        currentScroll = newScroll; 

        //Unset date 
        currentDate = false; 

        //Enable scrolling 
        unfreeze(); 

        /* 
        Wait 100ms before enabling the direction function again 
        (to prevent a loop from occuring). 
        */ 
        setTimeout(function() { 
         preventLoop = true; 
        }, 100); 
       }, 1000); 
      } 
     } 
    } 
    $(window).on("scroll", scroll); 
})(); 


Wykonanie C:

Wyjścia przewijania kierunek, dystans i szybkość konsolę, użytkownik może przewinąć ilość pikseli ustawioną w distance zmienna.

Jeśli użytkownik przewinie szybko, mogą przewinąć jeszcze kilka pikseli.

Demo on JSFiddle

(function iDirectionSpeedVelocity() { 
    var distance = 100; //pixels to scroll to determine speed 
    var preventLoop = true; 
    var currentScroll = []; 
    var currentDate = []; 
    function scroll() { 
     if(preventLoop) { 
      //Set date on scroll 
      currentDate.push(new Date()); 

      //Set scrollTop on scroll 
      currentScroll.push(scrollTop()); 

      var lastDate = currentDate[currentDate.length - 1]; 
      var lastScroll = currentScroll[currentScroll.length - 1]; 

      //User scrolled `distance` px or scrolled to the top/bottom 
      if(Math.abs(currentScroll[0] - lastScroll) >= distance || !lastScroll || lastScroll == scrollHeight()) { 
       //Stop scrolling 
       preventLoop = false; 
       freeze(currentScroll[currentScroll.length - 1]); 

       //Total time 
       console.log("Time: "+(lastDate.getTime() - currentDate[0].getTime())+"ms"); 

       //Total distance 
       console.log("Distance: "+Math.abs(lastScroll - currentScroll[0])+"px"); 

       /* 
       Calculate speeds between every registered scroll 
       (speed is described in milliseconds per pixel) 
       */ 
       var speeds = []; 
       for(var x = 0; x < currentScroll.length - 1; x++) { 
        var time = currentDate[x + 1].getTime() - currentDate[x].getTime(); 
        var offset = Math.abs(currentScroll[x - 1] - currentScroll[x]); 
        if(offset) { 
         var speed = time/offset; 
         speeds.push(speed); 
        } 
       } 

       //Output array of registered speeds (milliseconds per pixel) 
       console.log("speeds (milliseconds per pixel):"); 
       console.log(speeds); 

       /* 
       We can use the array of speeds to check if the speed is increasing 
       or decreasing between the first and last half as example 
       */ 
       var half = Math.round(speeds.length/2); 
       var equal = half == speeds.length ? 0 : 1; 
       var firstHalfSpeed = 0; 
       for(var x = 0; x < half; x++) { 
        firstHalfSpeed += speeds[x]; 
       } 
       firstHalfSpeed /= half; 
       var secondHalfSpeed = 0; 
       for(var x = half - equal; x < speeds.length; x++) { 
        secondHalfSpeed += speeds[x]; 
       } 
       secondHalfSpeed /= half; 
       console.log("average first half speed: "+firstHalfSpeed+"ms per px"); 
       console.log("average second half speed: "+secondHalfSpeed+"ms per px"); 
       if(firstHalfSpeed < secondHalfSpeed) { 
        console.log("conclusion: speed is decreasing"); 
       } else { 
        console.log("conclusion: speed is increasing"); 
       } 

       //Check direction 
       if(lastScroll > currentScroll[0]) { 
        console.log("scrolling down"); 
        //scroll down animation here 
       } else { 
        console.log("scrolling up"); 
        //scroll up animation here 
       } 

       /* 
       Time in milliseconds the scrolling is disabled, 
       in most cases this is equal to the time the animation takes 
       */ 
       setTimeout(function() { 
        //Unset scroll positions 
        currentScroll = []; 

        //Unset dates 
        currentDate = []; 

        //Enable scrolling 
        unfreeze(); 

        /* 
        Wait 100ms before enabling the direction function again 
        (to prevent a loop from occuring). 
        */ 
        setTimeout(function() { 
         preventLoop = true; 
        }, 100); 
       }, 2000); 
      } 
     } 
    } 
    $(window).on("scroll", scroll); 
})(); 


funkcje pomocnicze stosowane w powyższych implementacjach:

//Source: https://github.com/seahorsepip/jPopup 
function freeze(top) { 
    if(window.innerWidth > document.documentElement.clientWidth) { 
     $("html").css("overflow-y", "scroll"); 
    } 
    $("html").css({"width": "100%", "height": "100%", "position": "fixed", "top": -top}); 
} 
function unfreeze() { 
    $("html").css("position", "static"); 
    $("html, body").scrollTop(-parseInt($("html").css("top"))); 
    $("html").css({"position": "", "width": "", "height": "", "top": "", "overflow-y": ""}); 
} 
function scrollTop() { 
    return $("html").scrollTop() ? $("html").scrollTop() : $("body").scrollTop(); 
} 
function scrollHeight() { 
    return $("html")[0].scrollHeight ? $("html")[0].scrollHeight : $("body")[0].scrollHeight; 
} 

Po prostu spojrzeć na scrollify wspomniano w komentarzach, że to 10kB i wymaga haka na każdym prostym wydarzeniu: dotyk, przewijanie myszy, przyciski klawiatury itp.

To nie wydaje się zbyt przyszłościowe, kto wie, jaka interakcja użytkownika może spowodować przewijanie w przyszłości?

Z drugiej strony zdarzenie onscroll będzie zawsze wyzwalane po przewinięciu strony, więc po prostu podłączmy kod animacji, nie martwiąc się o interakcję z urządzeniem wejściowym.

+0

@Kaiido To prawie to samo, co napisałem (z wyjątkiem fantazyjnego rysunku na płótnie), ale niestety to nie działa na ekranach dotykowych, nawigacji na klawiaturze itp.Zdecydowałem się więc użyć zdarzenia przewijania tylko dlatego, że jest ono wyzwalane przez wszystko: P – seahorsepip

+1

Niestety pokazuje tylko 2 prędkości (szybkie lub wolne) w oknach:/Napisałem kilka rzeczy, które zostały wywołane podczas przewijania w przeszłość i nauczyłem się, że pisanie kodu dla każdego pojedynczego wydarzenia (dotyk, przewijanie itp.) staje się denerwujące i staje się dużo pracy: P Pisanie powyżej implementacji również wymagało trochę manipulowania, aby uzyskać pracę, ponieważ nie istnieje prawdziwa metoda zatrzymania przewijania , Musiałem użyć sztuczki pozycjonowania, której użyłem wcześniej. – seahorsepip

+0

Wtyczka myszy wyglądała obiecująco, ale błąd webkit uniemożliwia jej pracę w Safari, co jest problematyczne. – Robert

0

Jak twierdzi @seahorsepip, nie jest możliwe określenie miejsca przewijania bez dodawania niestandardowego zachowania z JavaScript.Docs MDN nie wymienione w dowolny sposób, aby uzyskać dostęp kolejce zdarzeń przewijania: https://developer.mozilla.org/en-US/docs/Web/Events/scroll

znalazłem te informacje pomocne: Normalizing mousewheel speed across browsers

Podkreśla trudności wiedząc, gdzie strona pójdzie na podstawie danych wprowadzonych przez użytkownika. Moja sugestia polega na wywołaniu przewijania do zdarzenia Y, gdy kod przewiduje osiągnięcie progu. W twoim przykładzie, jeśli zwojnica przesunęła stronę 800 z 1000 pikseli w oknie czasowym o długości 250ms, ustaw przewijanie do tego znaku o wartości 1000 pikseli i odetnij przewijanie przez 500 ms.

https://developer.mozilla.org/en-US/docs/Web/API/window/scrollTo

+0

Przewijanie ekranu i myszy nie jest tak wielkim problemem, jak pokazałem w mojej odpowiedzi, ale przewijanie touchpada to ZŁO. Szybkie przewijanie touchpadem wywoła zdarzenie przewijania przez prawie 3 sekundy. D: Co oznacza, że ​​jeśli zatrzymasz przewijanie, jak w moim przykładzie, animuj przez 500 ms, a następnie włącz przewijanie, będzie przewijane przez 2,5 sekundy po animacji: / – seahorsepip

0

nie jestem całkiem pewien, czy mam to, czego szukasz. Miałem kiedyś projekt, w którym musiałem kontrolować przewijanie. Wtedy nadpisałem domyślne zdarzenie przewijania, po którym można ustawić niestandardową odległość dla "jednego" przewijania. Dodatkowo dodano animacje jQuery, aby przewinąć do określonej pozycji. Tutaj można spojrzeć: http://c-k.co/zw1/ Jeśli tego szukasz możesz skontaktować się ze mną, a ja zobaczę, ile ja jeszcze zrozumieć własnej thingy tam

Powiązane problemy