Est-il possible d'animer scrollTop avec jQuery?

226

Je veux faire défiler en douceur. Je ne veux pas avoir à écrire une fonction pour cela - surtout si jQuery en a déjà une.

Bryan Field
la source

Réponses:

463

Vous pouvez simplement utiliser .animate()la scrollToppropriété, comme ceci:

$("html, body").animate({ scrollTop: "300px" });
Nick Craver
la source
Très bien .. pas besoin d'utiliser des plugins pour cela.
Amol M Kulkarni
15
Pourquoi avez-vous besoin des deux htmlet body? Il y a un aperçu ici: stackoverflow.com/questions/2123690/… , mais ce n'est pas une réponse complète.
Haralan Dobrev
28
body est utilisé par webkit, html est utilisé par firefox.
Jory
2
FYI: Si <html>a overflow-x:hidden;cette animation ne fonctionnera pas. (Pourrait ne pas fonctionner overflow-y:hidden, et overflow:hidden, mais je n'ai pas testé.
collinhaines
1
Je pense que cela a bodyfonctionné dans Chrome à partir du début de cette année, mais maintenant cela doit être html.
Nick Davies
73

La réponse de Nick fonctionne très bien. Soyez prudent lorsque vous spécifiez une fonction complete () à l'intérieur de l'appel animate () car elle sera exécutée deux fois car vous avez deux sélecteurs déclarés (html et body).

$("html, body").animate(
    { scrollTop: "300px" },
    {
        complete : function(){
            alert('this alert will popup twice');
        }
    }
);

Voici comment éviter le double rappel.

var completeCalled = false;
$("html, body").animate(
    { scrollTop: "300px" },
    {
        complete : function(){
            if(!completeCalled){
                completeCalled = true;
                alert('this alert will popup once');
            }
        }
    }
);
Kita
la source
1
La question est, pourquoi voudriez-vous animer "html, body" et pas seulement "body"?
Lior
21
Parce que selon le navigateur que vous utilisez, vous pouvez soit animer le corps OU html. En animant LES DEUX, vous couvrez plus de navigateurs.
Bene
Mais dans ce cas, il n'apparaîtra qu'une seule fois, même s'il doit être appelé à plusieurs reprises (cliquez sur l'événement sur le bouton) ou si j'ai raté quelque chose?
HoGo
Oui, dans ce cas, il n'apparaîtra qu'une seule fois. Si vous devez animer à plusieurs reprises, vous devez rendre la variable completeCalled locale à la fonction de rappel qui exécute la fonction d'animation. De cette façon, il n'apparaîtra qu'une seule fois lorsque vous cliquerez, mais il réapparaîtra (une seule fois) lorsque vous cliquerez à nouveau.
Kita
Pour éviter le déclenchement de la fonction de double rappel, vous pouvez utiliser la die()fonction avant live()l'appel de fonction. Cela garantit que votre live()gestionnaire ne sera appelé qu'une seule fois.
Lord Nighton
27

La réponse de Nick fonctionne très bien et les paramètres par défaut sont agréables, mais vous pouvez contrôler plus complètement le défilement en remplissant tous les paramètres facultatifs.

voici à quoi ça ressemble dans l'API:

.animate( properties [, duration] [, easing] [, complete] )

afin que vous puissiez faire quelque chose comme ça:

.animate( 
    {scrollTop:'300px'},
    300,
    swing,
    function(){ 
       alert(animation complete! - your custom code here!); 
       } 
    )

voici la page api de la fonction jQuery .animate: http://api.jquery.com/animate/

éclaireur
la source
15

Comme Kita l'a mentionné, il y a un problème avec le déclenchement de plusieurs rappels lorsque vous animez à la fois sur 'html' et sur 'body'. Au lieu d'animer les deux et de bloquer les rappels ultérieurs, je préfère utiliser une détection de fonctionnalité de base et animer uniquement la propriété scrollTop d'un seul objet.

La réponse acceptée sur cet autre fil donne un aperçu de la propriété scrollTop de l'objet que nous devrions essayer d'animer: défilement et animation de pageYOffset dans IE8

// UPDATE: don't use this... see below
// only use 'body' for IE8 and below
var scrollTopElement = (window.pageYOffset != null) ? 'html' : 'body';

// only animate on one element so our callback only fires once!
$(scrollTopElement).animate({ 
        scrollTop: '400px' // vertical position on the page
    },
    500, // the duration of the animation 
    function() {       
        // callback goes here...
    })
});

METTRE À JOUR - - -

La tentative de détection de fonctionnalité ci-dessus échoue. Il semble qu'il n'y ait pas de méthode sur une ligne, car la propriété pageYOffset des navigateurs de type webkit renvoie toujours zéro lorsqu'il y a un doctype. Au lieu de cela, j'ai trouvé un moyen d'utiliser une promesse de faire un seul rappel pour chaque exécution de l'animation.

$('html, body')
    .animate({ scrollTop: 100 })
    .promise()
    .then(function(){
        // callback code here
    })
});
Stephen
la source
1
L'utilisation de promise () est géniale.
Allan Bazinet
Cela nécessite plus de votes positifs. Je n'ai jamais vu de promesse utilisée pour cela auparavant, et c'est quelque chose que je finis par rechercher tous les quelques mois. Comme l'a dit Allan, le génie.
Tortilaman
14

J'ai ce que je crois être une meilleure solution que le $('html, body') hack.

Ce n'est pas un vol simple, mais le problème que j'ai rencontré $('html, body')est que si vous vous connectez$(window).scrollTop() pendant l'animation, vous verrez que la valeur saute partout, parfois de centaines de pixels (bien que je ne vois rien de tel) visuellement). J'avais besoin que la valeur soit prévisible, pour pouvoir annuler l'animation si l'utilisateur saisissait la barre de défilement ou faisait tourner la molette pendant le défilement automatique.

Voici une fonction qui animera le défilement en douceur:

function animateScrollTop(target, duration) {
    duration = duration || 16;
    var scrollTopProxy = { value: $(window).scrollTop() };
    if (scrollTopProxy.value != target) {
        $(scrollTopProxy).animate(
            { value: target }, 
            { duration: duration, step: function (stepValue) {
                var rounded = Math.round(stepValue);
                $(window).scrollTop(rounded);
            }
        });
    }
}

Vous trouverez ci-dessous une version plus complexe qui annulera l'animation sur l'interaction avec l'utilisateur, ainsi que le refiring jusqu'à ce que la valeur cible soit atteinte, ce qui est utile lorsque vous essayez de définir le scrollTop instantanément (par exemple, simplement en appelant $(window).scrollTop(1000)- d'après mon expérience, cela ne fonctionne pas) 50% du temps.)

function animateScrollTop(target, duration) {
    duration = duration || 16;

    var $window = $(window);
    var scrollTopProxy = { value: $window.scrollTop() };
    var expectedScrollTop = scrollTopProxy.value;

    if (scrollTopProxy.value != target) {
        $(scrollTopProxy).animate(
            { value: target },
            {
                duration: duration,

                step: function (stepValue) {
                    var roundedValue = Math.round(stepValue);
                    if ($window.scrollTop() !== expectedScrollTop) {
                        // The user has tried to scroll the page
                        $(scrollTopProxy).stop();
                    }
                    $window.scrollTop(roundedValue);
                    expectedScrollTop = roundedValue;
                },

                complete: function () {
                    if ($window.scrollTop() != target) {
                        setTimeout(function () {
                            animateScrollTop(target);
                        }, 16);
                    }
                }
            }
        );
    }
}
John Starr Dewar
la source
7

J'avais des problèmes où l'animation commençait toujours à partir du haut de la page après un rafraîchissement de la page dans les autres exemples.

J'ai corrigé cela en n'animant pas directement le CSS mais en appelant plutôt window.scrollTo();à chaque étape:

$({myScrollTop:window.pageYOffset}).animate({myScrollTop:300}, {
  duration: 600,
  easing: 'swing',
  step: function(val) {
    window.scrollTo(0, val);
  }
});

Cela permet également de contourner le problème htmlvs bodycar il utilise du JavaScript multi-navigateur.

Jetez un œil à http://james.padolsey.com/javascript/fun-with-jquerys-animate/ pour plus d'informations sur ce que vous pouvez faire avec la fonction d'animation de jQuery.

compliste
la source
5

Vous pouvez utiliser l'animation jQuery pour la page de défilement avec une durée spécifique:

$("html, body").animate({scrollTop: "1024px"}, 5000);

où 1024px est le décalage de défilement et 5000 est la durée des animations en millisecondes.

Alessandro Pirovano
la source
Très bonne réponse! Heureux que vous ayez inclus l'option de durée. Je voulais juste ajouter que cela $("html, body").animate({scrollTop: 1024}, 5000);fonctionne également.
Ryan Taylor
4

Essayez le plugin scrollTo .

Tatu Ulmanen
la source
Il semble que scrollTo ne soit plus un projet sur ce site.
Jim
Le code pour accomplir cela est si simple. Pourquoi voudriez-vous ajouter un plugin pour faire quelque chose que vous pouvez faire avec une seule ligne de code?
Gavin
4
Il y a quatre ans, ce n'était pas si simple .. :)
Tatu Ulmanen
4
$(".scroll-top").on("click", function(e){
   e.preventDefault();
   $("html, body").animate({scrollTop:"0"},600);
});
Rubel Hossain
la source
1

Si vous souhaitez descendre à la fin de la page (vous n'avez donc pas besoin de faire défiler vers le bas), vous pouvez utiliser:

$('body').animate({ scrollTop: $(document).height() });
Petite fourmi
la source
0

Mais si vous voulez vraiment ajouter de l'animation pendant le défilement, vous pouvez essayer mon plugin simple ( AnimateScroll ) qui prend actuellement en charge plus de 30 styles d' accélération

Ram Patra
la source
-3

le code du navigateur croisé est:

$(window).scrollTop(300); 

c'est sans animation mais fonctionne partout

user2760861
la source
6
Je cherchais un moyen "d'animer scrollTop avec jQuery?" Une réponse «sans animation» n'est pas une réponse.
Bryan Field