2012-01-04 6 views
7

estoy usando bind/Unbind rueda del ratón para desplazarse, a partir de esta respuesta SO:Herramientas jQuery desplazables con rueda del ratón - Desplazarse una posición y dejar de

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

estoy un túnel hasta el árbol de eventos del delta, para apuntar solo a los valores de X mousewheel. Todo está funcionando bien. El problema que intento superar: simplemente quiero avanzar/retroceder UN PANEL, luego dejar de desplazarme. Actualmente, estoy desvinculando el evento mousewheel inmediatamente después del movimiento, y eso efectivamente detiene el desplazamiento ... pero al desvincular el evento mousewheel también se activa la página. Lo que necesito es poder oler el primer valor deltaX para la dirección, luego hacer un movimiento y dejar de escuchar. ¿Debo buscar autoscroll para obtener respuestas? Encuadernar/desatar se siente pesado, pero no puedo, por mi vida, imaginar cómo patear después de un solo movimiento, mientras sigo siendo capaz de desplazarme luego de que ese movimiento se haya completado. Aquí está mi función de la rueda del ratón:

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

También he mirado establecimiento de un temporizador, al estilo de:

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

que parecía muy prometedor, pero no-go. Aquí está la página del complemento para este tipo: http://brandonaaron.net/code/mousewheel/docs

Gracias por echarle un vistazo.

Respuesta

18

Dado que el DOM no ofrece ninguna forma de diferenciar entre el primer evento de desplazamiento disparado y los subsecuentes que pasan a ser parte del mismo movimiento de desplazamiento, estamos obligados a pensar en métodos indirectos para distinguirlos.

Si se desplaza rápidamente por cualquier elemento en particular, se desencadena un evento de desplazamiento muchas veces en secuencia. Usando el siguiente código, podemos tener una idea sobre exactamente la frecuencia con que ocurre:

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

Cuando se desplaza sobre ese div, usted obtendrá una salida de la consola que tiene este aspecto:

// Using mouse wheelbar 
251327626600149 
251327626600215 
251327626600265 
251327626600282 
251327626600332 
251327626600365 

// Using touchpad 
251327626626207 
251327626626225 
251327626626261 
251327626626276 
251327626626312 
251327626626345 

Al observar esta salida, parece que los eventos mousescroll se disparan generalmente entre 20 ms y 60 ms el uno del otro. Para estar seguros, tomaremos el extremo superior para ser de 100 ms. Esto es muy informativo, porque podemos usarlo para distinguir entre eventos de desplazamiento que son parte de la misma acción y aquellos que probablemente sean distintos e iniciados deliberadamente por el usuario.

Lo que podría hacer aquí es crear una variable de "marca de tiempo" globalmente accesible, actualizándola cada vez que se active un evento mousescroll, sea exitoso o no. Algo como esto:

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

Esto ignora efectivamente todos mousescroll eventos que se disparan después del evento inicial que todos ellos precedido, pero comienza a trabajar de nuevo después de que el usuario ha hecho una pausa de 100 ms.

Esto resolvería su problema, excepto si su función scrollToSomeOtherDiv() involucró algún tipo de animación que consume mucho tiempo. Por supuesto, puede hacer una booleana global isAnimating, y verifique si es verdadera cada vez que se activa un evento mousescroll (asegurándose de convertirlo en falso en una devolución de llamada una vez que la animación haya terminado).

Eso funcionaría, excepto que podría proporcionar una experiencia desagradable para el usuario. Alguien que quiera desplazarse rápidamente por dos paneles probablemente no pause entre pergaminos, incluso después de ver comenzar la animación. El código de arriba verá todos sus eventos mousescroll como parte del mismo movimiento de desplazamiento y continuará ignorándolos.

En ese caso, simplemente podría usar el tiempo de animación como su umbral. Establece un timeStamp una vez que se inicia la animación y luego ignora todos los eventos mousescroll durante ese período de tiempo. He escrito un ejemplo aquí: http://jsfiddle.net/Sg8JQ/

El código en cuestión está aquí:

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

También he incluido quietPeriod que es el tiempo más allá del tiempo de animación durante el cual desea seguir ignorando mousescroll eventos . Puede establecerlo en 0 si desea que el desplazamiento sea "receptivo" tan pronto como se complete la animación.

+0

Cada solución que ideé implicaba establecer algunas En el proyecto en cuestión, los mods de diseño finalmente negaron el problema, pero todavía me ha afectado. Justo ayer tuve un problema en el mismo estadio, no similar, en el que necesitaba detener un anima te() en el tamaño de la ventana. Volví a mi mente sobre este tema. Creo que lo que ha proporcionado es la cosa más sexy/más probable que he encontrado hasta la fecha. Por eso, te señalo el ganador. – wlangley

+1

¡Impresionante! Llamar un pedazo de código 'sexy' es tanto un cumplido como puedo manejar. Sin embargo, estaba pensando en tu idea de diseño inicial: con el desplazamiento horizontal, aún te toparían algunos problemas con los gestos en mac (ya que dos movimientos de dedo se desplazan y te llevan de regreso en la historia en Chrome y Safari). Todavía tengo que encontrar una solución para ese problema. – pikappa

+0

Con valores inferiores a 500 (hablando de animationTime y quietPeriod), este patrón deja de funcionar. vuelve a intentar tu jsfiddle con valores bajos y verás el punto donde, en mi opinión, deja de funcionar como se esperaba. Solución apreciada si alguna – Ben

0

Eche un vistazo a esto y vea lo que piensa. Para empezar, no me vincularía a mousewheel porque Firefox utiliza DOMMouseScroll en su lugar. Esto también usa un manejador delta diferente que es el inverso del usado por todos los otros navegadores. En lugar de ello, conéctese al evento de desplazamiento de jQuery que normaliza el comportamiento. Puede rastrear la última posición de desplazamiento de desplazamiento para determinar si el usuario se desplazó hacia arriba o hacia abajo.

Estoy suponiendo que estás animando entre secciones. ¿Hay alguna función de devolución de llamada que pueda usar para determinar si debe desplazarse? Creé un global para rastrear si los elementos están actualmente animando. Si es así, no nos molestamos en ejecutar la función. Finalmente, noté que la devolución de llamada para la animación de desplazamiento parece activarse antes de que ocurra el evento de desplazamiento final, así que de hecho tuve que usar un setTimeout allí. No me gustó, pero no pude entender cómo hacer que la devolución de llamada funcione correctamente.

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

Su suposición es correcta - Tengo un 1 -página, y estoy desplazándose entre secciones, pero el sitio/desplazamiento es horizontal. Entonces, necesitaría usar scrollLeft en lugar de scrollTop, supongo. Pero el problema más grande es que hay una propiedad del objeto Scroll de herramientas llamado 'mousewheel' (http://flowplayer.org/tools/demos/scrollable/plugins/index.html), y si no uso eso (set true o bind to the end), al desplazar la rueda del mouse se destruye el sitio. Parece que debería haber una manera de posponer la acción (scrollable.move()) hasta que la deltaX deje de cambiar y luego se mueva una vez. – wlangley

+0

Me pregunto si .throttle() podría ser mi bala mágica. http://benalman.com/code/projects/jquery-throttle-debounce/examples/throttle/ – wlangley

+0

Hmm, estoy atascado. No estoy seguro de cómo resolvería esto = ( – mrtsherman

0

lo tanto, esto es lo que estoy haciendo. Parece que funciona, pero todavía tiene errores.

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

}); 

Así, mientras que el aumento de i imprime bien, si yo elimine el comentario si i = 4 línea de impresión, no funciona correctamente (es posible que tenga que desplazarse difícil si se está utilizando un ratón, im intentando en mi trackpad). Mientras lo entiendo mientras aumente continuamente a medida que obtienes el valor delta, solo ejecutas algo cuando llego a 4 y solo restableces su valor si el desplazamiento se detiene durante 250 ms, este código parece reiniciar i a 0 al menos dos o tres veces, así que == 4 en realidad se ejecuta dos o tres veces. Quizás puedas encontrar lo que podría estar pasando. Estoy casi allí, solo necesito arreglar ese error.

Intenta agregar algo que cambie como efecto fadeIn, fadeOut dentro de esa sentencia if (i == 4) y es más prominente.

EDIT:

Heres the new edited version.El enlace del mousewheel era viejo, así que quizás esto lo muestre mejor ahora. Si se mueve rápido, lo verá parpadeando ...

+0

¿Se supone que esto es una respuesta al OP o está publicando su propia pregunta? Esto no es un foro de discusión. – Sparky

+0

Esta es una respuesta. – Chandra

0

Creo que la solución proporcionada por "pikappa" es pura sensualidad.

Para mí funcionaba perfectamente en Firefox pero estaba experimentando un "parpadeo" mientras se desplazaba por Chrome 26.0.x

Un simple sucia "parche" a su código sorprendente es la adición de "return false;" en línea 57 y 64 de su jsFiddle

Cuestiones relacionadas