AngularJS - $anchorScroll smooth/duration

2019-01-10 02:37发布

问题:

Reading the AngularJS docs I haven't figured out if $anchorScroll can have a duration/easing option to smooth scroll to elements.

It only says:

$location.hash('bottom');

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

I do not use jquery and don't want to; is there still a clever yet simple way to make or extend $anchorScroll in order to make scrolling more smooth?

回答1:

Unfortunately this is not possible using $anchorScroll. As you discovered $anchorScroll doesn't have any options and doesn't work with $ngAnimate. In order to animate the scroll you would need to use your own service/factory or just straight javascript.

For the sake of self-learning I put together an example with a smooth scrolling service. There are probably better ways to do this so any feedback is encouraged.

To scroll to an element you attach a ng-click="gotoElement(ID)" to any element. I think an even better route would be to make this a directive.

Here's the working example on jsFiddle.

Update

There are now a number of third-party directives for accomplishing this.

  • https://github.com/oblador/angular-scroll.
  • https://github.com/d-oliveros/ngSmoothScroll
  • https://github.com/arnaudbreton/angular-smoothscroll
  • https://gist.github.com/justinmc/d72f38339e0c654437a2


回答2:

You can also use the angular-scroll, link "https://github.com/durated/angular-scroll/". It is smooth scrolling also few easing functions to get a professional look.



回答3:

The answer from Brett worked great for me. I did some small changes on his solution in terms of modularization and testability.

Here's is yet another working example on JsFiddle that includes the other version with testing included.

For testing, I'm using Karma and Jasmine. Signature has been slightly modified as follows:

 anchorSmoothScroll.scrollTo(elementId, speed);

Where element is a mandatory attribute to scroll to and speed is optional where the default is 20 (as it was before).



回答4:

You can also use ngSmoothScroll, link: https://github.com/d-oliveros/ngSmoothScroll.

Just include the smoothScroll module as a dependency and use it like this:

<a href="#" scroll-to="my-element-3">Click me!</a>



回答5:

None of the solutions here actually does what OP originally asked, that is, make $anchorScroll scrolling smoothly. Difference between smooth scrolling directives and $anchroScroll is that it uses/modifies $location.hash(), which may be desirable in some cases.

Here is gist for simple module that replaces $anchorScroll scrolling with smooth scrolling. It uses https://github.com/oblador/angular-scroll library for the scrolling itself (replace it with something else if you want, it should be easy).

https://gist.github.com/mdvorak/fc8b531d3e082f3fdaa9
Note: It actually does not get $anchorScroll to scroll smoothly, but it replaces its handler for scrolling.

Enable it simply by referencing mdvorakSmoothScroll module in your application.



回答6:

Alan, thank you. If anyone interested, I formatted it based on John Pappa standards.

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

        }

    };

};

})();


回答7:

I am not aware of how to animate $anchorScroll . Here's how I do it in my projects:

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

And the JS function:

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