É possível animar scrollTop com jQuery?

226

Eu quero rolar suavemente para baixo. Eu não quero ter que escrever uma função para isso - especialmente se o jQuery já tiver uma.

Bryan Field
fonte

Respostas:

463

Você pode apenas usar .animate()a scrollToppropriedade, assim:

$("html, body").animate({ scrollTop: "300px" });
Nick Craver
fonte
Muito bom .. não há necessidade de usar plugins para isso.
Amol M Kulkarni
15
Por que você precisa de ambos htmle body? Há algumas dicas aqui: stackoverflow.com/questions/2123690/… , mas não é uma resposta completa.
Haralan Dobrev
28
body é usado pelo webkit, o html é usado pelo firefox.
Jory
2
FYI: Se <html>tiver overflow-x:hidden;este animal, não funcionará. (Pode não funcionar para overflow-y:hidden, e overflow:hidden, mas eu não testei.
collinhaines
1
Acho que bodyfuncionou no Chrome no início deste ano, mas agora tem que ser html.
Nick Davies
73

A resposta de Nick funciona muito bem. Tenha cuidado ao especificar uma função complete () dentro da chamada animate () porque ela será executada duas vezes desde que você tenha dois seletores declarados (html e corpo).

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

Veja como você pode evitar o retorno de chamada duplo.

var completeCalled = false;
$("html, body").animate(
    { scrollTop: "300px" },
    {
        complete : function(){
            if(!completeCalled){
                completeCalled = true;
                alert('this alert will popup once');
            }
        }
    }
);
Kita
fonte
1
A questão é: por que você animaria 'html, body' e não apenas 'body'?
Lior 10/10
21
Porque, dependendo do navegador que você é, você pode animar o corpo OU html. Ao animar AMBOS, você cobre mais navegadores.
Bene
Mas, neste caso, ele realmente aparecerá apenas uma vez, também se tiver que ser chamado repetidamente (clique no botão de evento) ou perdi alguma coisa?
HoGo 10/12/13
Sim, nesse caso, ele aparecerá apenas uma vez. Se você precisar animar repetidamente, tornaria a variável completeCalled local para a função de retorno de chamada que executa a função animar. Dessa forma, ele ainda aparecerá apenas uma vez quando você clicar, mas aparecerá novamente (apenas uma vez) quando você clicar novamente.
Kita
Para evitar o disparo duplo da função de retorno de chamada, você pode usar a die()função antes da live()chamada de função. Isso garante que seu live()manipulador será chamado apenas uma vez.
Lord Nighton 23/11/2015
27

A resposta de Nick funciona muito bem e as configurações padrão são boas, mas você pode controlar mais completamente a rolagem, completando todas as configurações opcionais.

aqui está o que parece na API:

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

para que você possa fazer algo assim:

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

aqui está a página da API da função janery .animate: http://api.jquery.com/animate/

desbravador
fonte
15

Como Kita mencionou, há um problema com vários retornos de chamada acionados quando você anima em 'html' e 'body'. Em vez de animar ambos e bloquear retornos de chamada subseqüentes, prefiro usar alguma detecção básica de recurso e animar apenas a propriedade scrollTop de um único objeto.

A resposta aceita nesse outro thread fornece algumas dicas sobre qual propriedade scrollTop do objeto devemos tentar animar: pageYOffset Scrolling and Animation no 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...
    })
});

ATUALIZAÇÃO - - -

A tentativa acima na detecção de recurso falha. Parece que não há uma maneira de fazer uma linha como a propriedade pageYOffset de navegadores do tipo kit da web sempre retorna zero quando há um tipo de documento. Em vez disso, encontrei uma maneira de usar uma promessa de fazer um único retorno de chamada para cada vez que a animação é executada.

$('html, body')
    .animate({ scrollTop: 100 })
    .promise()
    .then(function(){
        // callback code here
    })
});
Stephen
fonte
1
O uso de promessa () aqui é genial.
Allan Bazinet
Isso precisa de mais votos. Eu nunca vi promessa usada para isso antes, e isso é algo que acabo procurando a cada poucos meses. Como Allan disse, gênio.
Tortilaman
14

Eu tenho o que acredito ser uma solução melhor do que o $('html, body')hack.

Não é uma frase única, mas o problema que tive $('html, body')foi que, se você registrar $(window).scrollTop()durante a animação, verá que o valor salta por todo o lugar, às vezes centenas de pixels (embora eu não veja nada parecido) visualmente). Eu precisava que o valor fosse previsível, para poder cancelar a animação se o usuário agarrasse a barra de rolagem ou girasse a roda do mouse durante a rolagem automática.

Aqui está uma função que animará a rolagem sem problemas:

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

Abaixo está uma versão mais complexa que cancelará a animação na interação com o usuário, além de reajustar até que o valor alvo seja atingido, o que é útil ao tentar definir o scrollTop instantaneamente (por exemplo, simplesmente chamando $(window).scrollTop(1000)- na minha experiência, isso não funciona sobre). 50% do tempo.)

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

Eu estava tendo problemas em que a animação sempre começava do topo da página após uma atualização da página nos outros exemplos.

Corrigi isso não animando o css diretamente, mas chamando window.scrollTo();cada etapa:

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

Isso também contorna o problema htmlvs body, pois ele usa JavaScript em vários navegadores.

Veja http://james.padolsey.com/javascript/fun-with-jquerys-animate/ para obter mais informações sobre o que você pode fazer com a função animar do jQuery.

complistic
fonte
5

Você pode usar a animação jQuery para a página de rolagem com uma duração específica:

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

onde 1024px é o deslocamento da rolagem e 5000 é a duração das animações em milissegundos.

Alessandro Pirovano
fonte
Ótima resposta! Que bom que você incluiu a opção de duração. Só queria adicionar que $("html, body").animate({scrollTop: 1024}, 5000);também funciona.
Ryan Taylor
4

Experimente o plugin scrollTo .

Tatu Ulmanen
fonte
Parece que scrollTo não é mais um projeto nesse site.
Jim
O código para fazer isso é muito simples. Por que você adicionaria um plugin para fazer algo que você pode fazer com uma linha de código?
Gavin
4
Quatro anos atrás, não era tão simples .. :)
Tatu Ulmanen
4
$(".scroll-top").on("click", function(e){
   e.preventDefault();
   $("html, body").animate({scrollTop:"0"},600);
});
Rubel Hossain
fonte
1

Se você quiser mover para baixo no final da página (para não precisar rolar para baixo), use:

$('body').animate({ scrollTop: $(document).height() });
Formiga pequena
fonte
0

Mas se você realmente quiser adicionar alguma animação durante a rolagem, tente o meu plugin simples ( AnimateScroll ), que atualmente suporta mais de 30 estilos de atenuação

Ram Patra
fonte
-3

o código entre navegadores é:

$(window).scrollTop(300); 

é sem animação, mas funciona em qualquer lugar

user2760861
fonte
6
Eu estava procurando uma maneira "de animar scrollTop com jQuery?" Uma resposta que é "sem animação" não é uma resposta.
Bryan Campo