等到scrollTo完成后再运行命令

问题描述 投票:0回答:5

我有一个 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(); }; }]); }());

	
javascript angularjs
5个回答
7
投票

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

smoothScroll

函数可以这样定义,而不需要使用很多计时器(实际定义的计时器只是为了

reject()
承诺如果由于某种原因(例如用户交互)滚动失败):
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


2
投票
阅读此处

了解有关 $q 的更多信息。 anchorSmoothScroll.scrollTo(eID) .then(function() { $('#comment-input').focus(); })

编辑:您的代码失败了,因为您正在 setTimeout 内执行scrollTo,而这就是您必须解决您的承诺的地方。现在,下一个问题是有多个 setTimeouts(因为它在 for 循环内),因此会有很多承诺,而这正是 $q.all 可以提供帮助的地方。

我已经为你设置了一个plunker,我在其中更新了其中一个执行路径...看看

这里

。在控制台窗口中,滚动完成后您将看到焦点是打印机。为了使其显而易见,我将 setTimeout 间隔硬编码为 2 秒。我希望这有帮助。


1
投票
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

了解更多


0
投票
进行通知

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



0
投票
这个答案

对我不起作用;我在 body 上使用 overflow: hidden,似乎在某些移动浏览器上将

scrollY
scrollX
设置为
0
; 就我而言,我需要从
top
left
滚动,所以我的代码解释了这一点;这是我的第一次尝试:
  let vh = window.innerHeight;
  let vw = window.innerWidth;
  let e = component.element;
  let left = e.offsetLeft - vw / 2 + e.width / 2;
  let top = e.offsetTop - vh / 2 + e.height / 2;
  window.scrollTo({
    left: left,
    top: top,
    behavior: "smooth",
  });
  return await new Promise((resolve, reject) => {
    let failed = setTimeout(() => {
      console.log({ scrollX: window.scrollX, scrollY: window.scrollY });
      reject();
    }, 2000);
    let scrollHandler = () => {
      if (abs(window.scrollY - top) < 1 && abs(window.scrollX - left) < 1) {
        window.removeEventListener("scroll", scrollHandler);
        clearTimeout(failed);
        resolve();
      }
    };
    window.addEventListener("scroll", scrollHandler);
    scrollHandler();
  });

我从
这个答案

中发现了

"scrollend"
事件,但它不适用于所有移动浏览器; 这是我的代码,有时可以工作,但不适用于所有移动浏览器:

let e = component.element; let vh = window.innerHeight; let vw = window.innerWidth; let left = e.offsetLeft - vw / 2 + e.width / 2; let top = e.offsetTop - vh / 2 + e.height / 2; window.scrollTo({ left, top, behavior: "smooth", }); return await new Promise((resolve, reject) => { let failed = setTimeout(() => { console.log("scroll finish failed"); reject(); }, 2000); window.addEventListener("scrollend", scroll_handler); function scroll_handler() { clearTimeout(failed); window.removeEventListener("scrollend", scroll_handler); resolve(); } });

我计划采取解决方法:

通知用户使用支持的浏览器/操作系统;或使用

setTimeout

休眠指定的毫秒数,用户可以在应用程序的设置中配置该时间;滚动的最大时间是非常可预测的:用户在地图上移动角色,因此最多滚动一些像素

    

© www.soinside.com 2019 - 2024. All rights reserved.