AngularJS - $ anchorScroll smooth / duration


115

Lendo os documentos do AngularJS , não descobri se $anchorScrollpode haver uma opção de duração / atenuação para rolar suavemente para os elementos.

Diz apenas:

$location.hash('bottom');

// call $anchorScroll()
$anchorScroll();

Não uso o jquery e não quero; ainda existe uma maneira inteligente, mas simples de fazer ou estender a $anchorScrollfim de tornar a rolagem mais suave?

Respostas:


155

Infelizmente, isso não é possível usando $anchorScroll. Como você descobriu $anchorScroll, não tem opções e não funciona com $ngAnimate. Para animar o pergaminho, você precisará usar seu próprio serviço / fábrica ou apenas javascript direto.

Por uma questão de autoaprendizagem, criei um exemplo com um serviço de rolagem suave. Provavelmente, existem maneiras melhores de fazer isso, então qualquer feedback é incentivado.

Para rolar para um elemento, você anexa um ng-click="gotoElement(ID)"a qualquer elemento. Acho que um caminho ainda melhor seria tornar isso uma diretiva.

Aqui está o exemplo de trabalho no jsFiddle .

Atualizar

Existem agora várias diretivas de terceiros para fazer isso.


11
Muito agradável. Aqui está como uma diretiva: gist.github.com/justinmc/d72f38339e0c654437a2
Justin McCandless

@JustinMcCandless como você chama sua diretiva? Eu tentei: <a ng-click="anchor-smooth-school('about');"> Cerca de 1 </a> <a ng-click="anchorSmoothScroll('about');"> Cerca de 2 < / a>
Dan

1
@Dan just do<a anchor-smooth-scroll>About 1</a> <a anchor-smooth-scroll>About 2</a>
Justin McCandless

1
Legal, eu gosto dessa resposta. Mas isso ainda adiciona outra razão para odiar o AngularJS, quero dizer, olhe para o tamanho disso em comparação com o JQuery scrollTo
Felype

1
Para utilizar a directiva, criar um elemento com um ID (por exemplo <div id="my-div">my div</div>) e, em seguida, criar um link como este: <a anchor-smooth-scroll="my-div">visit my div</a>.
Jason Swett,

20

Você também pode usar a rolagem angular, link " https://github.com/durated/angular-scroll/ ". É uma rolagem suave e também algumas funções de atenuação para obter uma aparência profissional.


1
Este plugin funciona em outros elementos além de $ documents? Tentei aplicar scrollToElement a um div para poder rolar uma linha dentro dele para a visualização, mas não funcionou.
Shaunak

10

A resposta de Brett funcionou muito bem para mim. Fiz algumas pequenas mudanças em sua solução em termos de modularização e testabilidade.

Aqui está outro exemplo de trabalho no JsFiddle que inclui a outra versão com testes incluídos.

Para teste, estou usando Karma e Jasmine. A assinatura foi ligeiramente modificada da seguinte forma:

 anchorSmoothScroll.scrollTo(elementId, speed);

Onde element é um atributo obrigatório para o qual rolar e speed é opcional onde o padrão é 20 (como era antes).



2

Nenhuma das soluções aqui realmente faz o que o OP pediu originalmente, ou seja, torna a $anchorScrollrolagem suave. A diferença entre as diretivas de rolagem suave e $anchroScrollé que ela usa / modifica $location.hash(), o que pode ser desejável em alguns casos.

Aqui está a essência do módulo simples que substitui a rolagem $ anchorScroll por rolagem suave. Ele usa a biblioteca https://github.com/oblador/angular-scroll para a própria rolagem (substitua-a por outra se quiser, deve ser fácil).

https://gist.github.com/mdvorak/fc8b531d3e082f3fdaa9
Nota: Na verdade, não faz $ anchorScroll rolar suavemente, mas substitui seu manipulador para rolagem.

Habilite-o simplesmente referenciando o mdvorakSmoothScrollmódulo em seu aplicativo.


0

Alan, obrigado. Se alguém estiver interessado, eu formatei com base nos padrões John Pappa.

(function() {

'use strict';
var moduleId = 'common';
var serviceId = 'anchorSmoothScroll';

angular
    .module(moduleId)
    .service(serviceId, anchorSmoothScroll);

anchorSmoothScroll.$inject = ['$document', '$window'];

function anchorSmoothScroll($document, $window) {

    var document = $document[0];
    var window = $window;

    var service = {
        scrollDown: scrollDown,
        scrollUp: scrollUp,
        scrollTo: scrollTo,
        scrollToTop: scrollToTop
    };
    return service;

    function getCurrentPagePosition(currentWindow, doc) {
        // Firefox, Chrome, Opera, Safari
        if (currentWindow.pageYOffset) return currentWindow.pageYOffset;
        // Internet Explorer 6 - standards mode
        if (doc.documentElement && doc.documentElement.scrollTop)
            return doc.documentElement.scrollTop;
        // Internet Explorer 6, 7 and 8
        if (doc.body.scrollTop) return doc.body.scrollTop;
        return 0;
    }

    function getElementY(doc, element) {
        var y = element.offsetTop;
        var node = element;
        while (node.offsetParent && node.offsetParent !== doc.body) {
            node = node.offsetParent;
            y += node.offsetTop;
        }
        return y;
    }

    function scrollDown(startY, stopY, speed, distance) {

        var timer = 0;

        var step = Math.round(distance / 25);
        var leapY = startY + step;

        for (var i = startY; i < stopY; i += step) {
            setTimeout('window.scrollTo(0, ' + leapY + ')', timer * speed);
            leapY += step;
            if (leapY > stopY) leapY = stopY;
            timer++;
        }
    };

    function scrollUp(startY, stopY, speed, distance) {

        var timer = 0;

        var step = Math.round(distance / 25);
        var leapY = startY - step;

        for (var i = startY; i > stopY; i -= step) {
            setTimeout('window.scrollTo(0, ' + leapY + ')', timer * speed);
            leapY -= step;
            if (leapY < stopY) leapY = stopY;
            timer++;
        }
    };

    function scrollToTop(stopY) {
        scrollTo(0, stopY);
    };

    function scrollTo(elementId, speed) {

        var element = document.getElementById(elementId);

        if (element) {
            var startY = getCurrentPagePosition(window, document);
            var stopY = getElementY(document, element);

            var distance = stopY > startY ? stopY - startY : startY - stopY;

            if (distance < 100) {
                this.scrollToTop(stopY);

            } else {

                var defaultSpeed = Math.round(distance / 100);
                speed = speed || (defaultSpeed > 20 ? 20 : defaultSpeed);

                if (stopY > startY) {
                    this.scrollDown(startY, stopY, speed, distance);
                } else {
                    this.scrollUp(startY, stopY, speed, distance);
                }
            }

        }

    };

};

})();

0

Não sei como animar $anchorScroll. É assim que faço em meus projetos:

/* Scroll to top on each ui-router state change */
$rootScope.$on('$stateChangeStart', function() {
 scrollToTop();
});

E a função JS:

function scrollToTop() {
    if (typeof jQuery == 'undefined') {
        return window.scrollTo(0,0);
    } else {
        var body = $('html, body');
        body.animate({scrollTop:0}, '600', 'swing');
    }
    log("scrollToTop");
    return true;
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.