滚动到视图动画

57

我的代码在http://jsfiddle.net/mannagod/QT3v5/7/

JavaScript 代码如下:

function delay() {
    var INTENDED_MONTH = 7 //August
    // INTENDED_MONTH is zero-relative
    now = new Date().getDate(),
rows = document.getElementById('scripture').rows;
    if (new Date().getMonth() != INTENDED_MONTH) {
        // need a value here less than 1, 
        // or the box for the first of the month will be in Red
        now = 0.5
    };
    for (var i = 0, rl = rows.length; i < rl; i++) {
        var cells = rows[i].childNodes;
        for (j = 0, cl = cells.length; j < cl; j++) {
            if (cells[j].nodeName == 'TD'
  && cells[j].firstChild.nodeValue != ''
  && cells[j].firstChild.nodeValue == now) {
                // 'ffff99' // '#ffd700' // TODAY - red
                rows[i].style.backgroundColor = 'red' 
                rows[i].scrollIntoView();
            }
        }
    }
}

我需要找到一种平滑过渡到高亮行的方法,目前使用的 .scrollintoview() 会直接跳转到该行。需要动态替换 scrollintoview 实现。有什么想法吗?谢谢。

8个回答

149

8
嗯,Firefox比Chrome做得更好的一个方面。 - Wish
2
现在它可以在所有主要的浏览器中运行。唯一的兼容性问题是平滑行为不完全兼容:https://caniuse.com/#feat=scrollintoview - viery365
2
使用polyfill使scrollintoviewoptions在所有浏览器中都能正常工作,问题就解决了 :) https://dev59.com/XVgQ5IYBdhLWcg3wnFLJ#55357885 - metamagikum
建议第一行代码应该是 >>>var elmnt = document.getElementById("content"); - eeerrrttt
1
行为:“instant”不存在。它只能是“auto”或“smooth”。 - Andrew West

44

5
这是最佳答案。它可以平稳地滚动到所需位置。谢谢! - Yoraco Gonzales

15

也许您不想仅为了实现此功能而添加jQuery。 elem是要滚动的元素。 目标位置可以从要移入视图的元素的offsetTop属性中获取。

function Scroll_To(elem, pos)
{
    var y = elem.scrollTop;
    y += (pos - y) * 0.3;
    if (Math.abs(y-pos) < 2)
    {
        elem.scrollTop = pos;
        return;
    }
    elem.scrollTop = y;
    setTimeout(Scroll_To, 40, elem, pos);   
}

2
我遇到了无限递归的问题。我通过使用 Math.round((pos - y) * 0.3)Math.abs(y-pos) <= 2 来解决它。 - tastybytes

7

使用requestAnimationFrame实现平滑滚动,并在特定时间内完成,无需使用jQuery。

示例: http://codepen.io/Shadeness/pen/XXyvKG?editors=0010

window.bringIntoView_started = 0;
window.bringIntoView_ends = 0;
window.bringIntoView_y = 0;
window.bringIntoView_tick = function() {
  var distanceLeft, dt, duration, t, travel;
  t = Date.now();
  if (t < window.bringIntoView_ends) {
    dt = t - window.bringIntoView_started;
    duration = window.bringIntoView_ends - window.bringIntoView_started;
    distanceLeft = window.bringIntoView_y - document.body.scrollTop;
      travel = distanceLeft * (dt / duration);
      document.body.scrollTop += travel;
      window.requestAnimationFrame(window.bringIntoView_tick);
  } else {
    document.body.scrollTop = window.bringIntoView_y;
  }
};
window.bringIntoView = function(e, duration) {
  window.bringIntoView_started = Date.now();
  window.bringIntoView_ends = window.bringIntoView_started + duration;
  window.bringIntoView_y = Math.min(document.body.scrollTop + e.getBoundingClientRect().top, document.body.scrollHeight - window.innerHeight);
  window.requestAnimationFrame(window.bringIntoView_tick);
};

例如:

bringIntoView(document.querySelector('#bottom'), 400)

随着dt(deltaTime)的增加,它应该会加速,而distanceLeft越小,它则会减速。我考虑过如果用户滚动就退出循环,但觉得不必要。全局变量可以防止前一次调用完全接管,但无法取消前一个递归循环,因此它将以两倍速度进行动画。


奇怪。看起来你需要使用window.scrollTo(x, 0)而不是赋值给.scrollTop这里有另一个在StackOverflow上的答案 - Zren

4

你只需要包含这个polyfill,它就可以工作。

https://github.com/iamdustan/smoothscroll

<script src="js/smoothscroll.js"></script>

如果您使用npm,请要求它。

require('smoothscroll-polyfill').polyfill();

使用原生的scrollIntoView方法。
document.getElementById('parallax-group-logo').scrollIntoView({
    block: "start",
    behavior: "smooth"
});

3

如果有帮助的话,我想补充一下:

我正在为iOS和Android开发PWA,并使用scrollIntoView()方法,直到我发现Safari不支持scrollIntoViewOptions对象,因此无法平滑滚动等等。

我能够使用纯JS(或者说是TypeScript)模拟scrollIntoView的功能,包括平滑滚动和“nearest”选项,以便在iOS上使用。

点击处理程序或其他:

const element = *elementToScrollIntoView*;
const scrollLayer = *layerToDoTheScrolling*

if (/iPad|iPhone|iPod/.test(navigator.userAgent) {
    let position;
    const top = element.offsetTop - scrollLayer.scrollTop;
    if (element.offsetTop < scrollLayer.scrollTop) {
            // top of element is above top of view - scroll to top of element
        position = element.offsetTop;
    } else if (element.scrollHeight + top < scrollLayer.offsetHeight) {
            // element is in view - don't need to scroll
        return;
    } else if (element.scrollHeight > scrollLayer.offsetHeight) {
            // element is bigger than view - scroll to top of element
        position = element.offsetTop;
    } else {
            // element partially cut-off - scroll remainder into view
        const difference = scrollLayer.offsetHeight - (element.scrollHeight + top);
        position = scrollLayer.scrollTop - difference;
    }
        // custom function for iOS
    scrollToElement(scrollLayer, position, 200);
} else {
        // just use native function for Android
    element.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
}

手动滚动功能:
scrollToElement(scrollLayer, destination, duration) {
    if (duration <= 0) {
        return;
    }
    const difference = destination - scrollLayer.scrollTop;
    const perTick = (difference / duration) * 10;

    setTimeout(() => {
        scrollLayer.scrollTop = scrollLayer.scrollTop + perTick;
        if (scrollLayer.scrollTop === destination) {
            return;
        }
        scrollToElement(scrollLayer, destination, duration - 10);
    }, 10);
}

注意: 处理程序中的大量嵌套if语句和计算只是为了找到“最近”的位置,因为我试图复制该行为,但使用 scrollToElement 函数来动画滚动到顶部(默认行为没有 Options 对象),您可以使用以下内容:

scrollToElement(scrollLayer, element.offsetTop, 200);

2
你可以尝试在函数中添加evt.preventDefault()。这将覆盖正常的“点击链接”功能。例如:
    // Scroll to anchor ID using scrollTO event
function linkClicked(evt) { // function to listen for when a link is clicked
  evt.preventDefault();
  evt.scrollIntoView({behavior: 'smooth'});

}

// Scroll to section on link click
navBar.addEventListener('click', linkClicked);

2

试试这个:

function scroll_into_view_smooth(elem)
{   var FPS = 48; // frames per second
    var DURATION = 0.6; // sec
    var e = elem;
    var left = e.offsetLeft;
    var top = e.offsetTop;
    var width = e.offsetWidth;
    var height = e.offsetHeight;
    var body = document.body;
    var to_scroll = [];
    var p, offset;
    while ((p = e.offsetParent))
    {   var client_width = p.clientWidth;
        var client_height = p!=body ? p.clientHeight : Math.min(document.documentElement.clientHeight, window.innerHeight);
        if (client_width<p.scrollWidth-1 && ((offset=left-p.scrollLeft)<0 || (offset=left+width-p.scrollLeft-client_width)>0))
        {   to_scroll.push({elem: p, prop: 'scrollLeft', from: p.scrollLeft, offset: offset});
        }
        if (client_height<p.scrollHeight-1 && ((offset=top-p.scrollTop)<0 || (offset=top+height-p.scrollTop-client_height)>0))
        {   to_scroll.push({elem: p, prop: 'scrollTop', from: p.scrollTop, offset: offset});
        }
        e = p;
        left += e.offsetLeft;
        top += e.offsetTop;
    }
    var x = 0;
    function apply()
    {   x = Math.min(x+1/(DURATION * FPS), 1);
        for (var i=to_scroll.length-1; i>=0; i--)
        {   to_scroll[i].elem[to_scroll[i].prop] = to_scroll[i].from + to_scroll[i].offset*x*x;
        }
        if (x < 1)
        {   setTimeout(apply, 1000/FPS);
        }
    }
    apply();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接