angularjs 在运行命令之前,请等待scrollTo完成

wgeznvg7  于 10个月前  发布在  Angular
关注(0)|答案(4)|浏览(102)

我有一个AngularJS应用程序,我有一个平滑的滚动指令来强制页面滚动到底部。我希望命令只在滚动完成后运行。你可以看到,在我运行滚动功能的时候,我运行$('#comment-input').focus();来关注一个元素。我想改变它,所以这只在 * 之后 * 运行我知道我需要实现一个callback,但我不知道在哪里实现它。

(function() {

    var app = angular.module('myApp');

    app.service('anchorSmoothScroll', function(){

        this.scrollTo = function(eID) {

            // This scrolling function 
            // is from http://www.itnewb.com/tutorial/Creating-the-Smooth-Scroll-Effect-with-JavaScript

            var startY = currentYPosition();
            var stopY = elmYPosition(eID);
            var distance = stopY > startY ? stopY - startY : startY - stopY;
            if (distance < 100) {
                scrollTo(0, stopY); return;
            }
            var speed = Math.round(distance / 100);
            if (speed >= 20) speed = 20;
            var step = Math.round(distance / 25);
            var leapY = stopY > startY ? startY + step : startY - step;
            var timer = 0;
            if (stopY > startY) {
                for ( var i=startY; i<stopY; i+=step ) {
                    setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
                    leapY += step; if (leapY > stopY) leapY = stopY; timer++;
                } return;
            }
            for ( var i=startY; i>stopY; i-=step ) {
                setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
                leapY -= step; if (leapY < stopY) leapY = stopY; timer++;
            }

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

            function elmYPosition(eID) {
                var elm = document.getElementById(eID);
                var y = elm.offsetTop;
                var node = elm;
                while (node.offsetParent && node.offsetParent != document.body) {
                    node = node.offsetParent;
                    y += node.offsetTop;
                } return y;
            }

        };

    });

    app.controller('TextareaController', ['$scope','$location', 'anchorSmoothScroll',
    function($scope, $location, anchorSmoothScroll) {

        $scope.gotoElement = function (eID){
          // set the location.hash to the id of
          // the element you wish to scroll to.
          $location.hash('bottom-div');

          // call $anchorScroll()
          anchorSmoothScroll.scrollTo(eID);

          $('#comment-input').focus();

        };

    }]);

}());

字符串

oipij1gg

oipij1gg1#

我建议创建一个返回promise并避免循环/计时器的函数。然后你可以像这样访问函数:

smoothScroll(element).then(() => {
      //Execute something when scrolling has finished
    });

字符串
smoothScroll函数可以这样定义,而不需要使用很多计时器(实际定义的计时器只是为了在滚动由于某种原因失败时reject() promise,例如用户交互):

function smoothScroll(elem, offset = 0) {
  const rect = elem.getBoundingClientRect();
  let targetPosition = Math.floor(rect.top + self.pageYOffset + offset);
  window.scrollTo({
    top: targetPosition,
    behavior: 'smooth'
  });

  return new Promise((resolve, reject) => {
    const failed = setTimeout(() => {
      reject();
    }, 2000);

    const scrollHandler = () => {
      if (self.pageYOffset === targetPosition) {
        window.removeEventListener("scroll", scrollHandler);
        clearTimeout(failed);
        resolve();
      }
    };
    if (self.pageYOffset === targetPosition) {
      clearTimeout(failed);
      resolve();
    } else {
      window.addEventListener("scroll", scrollHandler);
      elem.getBoundingClientRect();
    }
  });
}


我还在这支笔中做了一个工作示例:https://codepen.io/familjenpersson/pen/bQeEBX

iovurdzv

iovurdzv2#

获取anchorSmoothScroll.scrollTo以返回一个promise(使用$q),然后在promise被填充后聚焦。Read here获取关于$q的更多信息。

anchorSmoothScroll.scrollTo(eID)
     .then(function() {
          $('#comment-input').focus();
     })

字符串
编辑:你的代码失败了,因为你在setTimeout中执行scrollTo,这就是你必须解决promise的地方。现在,下一个问题是有多个setTimeout(因为它在for循环中),所以会有很多promise,这就是$q.all会帮助的地方。
我已经为你设置了一个plunker,在那里我已经更新了一个执行路径.看看here。在控制台窗口中,你会看到,在滚动完成后,聚焦是打印机。为了使它明显,我已经硬编码设置间隔为2秒。我希望这有帮助。

kognpnkq

kognpnkq3#

要实现平滑滚动和聚焦,您不需要等待/添加滚动回调,只需使用element.focus({ preventScroll: true });
例如:

element.scrollIntoView({ behavior: "smooth" });
// also works for window.scrollTo({ top: y, behavior: 'smooth' })
element.focus({ preventScroll: true });

字符串
通过这种方式,您可以避免浏览器特定的问题,并允许更快的交互和即时焦点。
更多信息请访问https://github.com/w3c/csswg-drafts/issues/3744#issuecomment-685683932

fykwrbwg

fykwrbwg4#

您最终可以尝试绑定一个侦听器,以便在您到达底部时通知您,

elm.on('scroll', function() {
      if(elm[0].scrollTop === elm[0].scrollHeight)
       $('#comment-input').focus();
    });

字符串

相关问题