Javascript 如何使同步两个div滚动位置更平滑

Javascript 如何使同步两个div滚动位置更平滑,javascript,iscroll,Javascript,Iscroll,我正在尝试同步两个可滚动的div滚动位置 方法如下: :在滚动事件上设置其他DIV的滚动顶部。 问题:在iOS safari中,滚动事件在末尾执行,UI运行缓慢 :使用setInterval同步两个滚动位置。 问题:iOS在滚动期间不执行计时器功能, 因此,滚动位置在末尾同步。同样,这是更缓慢的。 尝试过了,计时器修复了许多博客中提到的问题,但仍然没有优雅 :已尝试自定义滚动条,因此在scroll事件上iScroll并尝试同步两者, 问题:这似乎更好,但在iOS中仍然是缓慢的 :已尝试自定义滚动

我正在尝试同步两个可滚动的div滚动位置

方法如下:

:在滚动事件上设置其他DIV的滚动顶部。 问题:在iOS safari中,滚动事件在末尾执行,UI运行缓慢

:使用setInterval同步两个滚动位置。 问题:iOS在滚动期间不执行计时器功能, 因此,滚动位置在末尾同步。同样,这是更缓慢的。 尝试过了,计时器修复了许多博客中提到的问题,但仍然没有优雅

:已尝试自定义滚动条,因此在
scroll
事件上iScroll并尝试同步两者, 问题:这似乎更好,但在iOS中仍然是缓慢的

:已尝试自定义滚动条,因此在
scroll
事件上iScroll并尝试同步两者, 问题:使用了iScroll,但使用了计时器,这取决于onScroll事件, 但在touchmove期间,iOS忙于提供动画 而是在touchend之前执行所需的计时器。 下面的代码引用了此方法。它也很迟钝

var active = .., other = ...
// active : active Scrolling element
// other : Element to be in sync with active
window.setInterval(function () {
    var y;
    if (active) {
        y = active.y;
    } else {
        return;
    }
    var percentage = -y / (active.scrollerHeight - active.wrapperHeight);
    var oscrollTop = percentage * (other.scrollerHeight - other.wrapperHeight);
    if (-other.maxScrollY >= toInt(oscrollTop)) {
        other.scrollTo(0, -toInt(oscrollTop));
    }
}, 20);

如何使两个可滚动div的同步滚动位置更平滑。请给我一些建议,这让我很恼火。

依靠滚动事件(OPs方法1)对于桌面实现来说是不错的。在屏幕更新之前触发滚动事件。在移动设备上,尤其是在iOS上,情况并非如此。由于资源有限,滚动事件仅在用户完成(抬起手指)滚动操作后触发

实现手动滚动 要在用户在iOS上滚动时发生滚动事件,需要手动实现滚动

  • 注册
    touchstart
    事件。第一次接触:

    var element1 = document.getElementById('content1');
    var element2 = document.getElementById('content2');
    
    var activeTouch = null;
    var touchStartY = 0;
    var element1StartScrollTop = 0;
    var element2scrollSyncFactor = 0;
    
    document.addEventListener('touchstart', function(event) {
        event.preventDefault();
    
        var touch = event.changedTouches[0];
    
        if ( activeTouch == null ) {
            // implement check if touch started on an element you want to be scrollable
            // save a reference to the scrolling element for the other functions
            activeTouch = touch;
            touchStartY = touch.screenY;
            // if scroll content does not change do this calculation only once to safe compute and dom access time while animating
            calcSyncFactor();
        }
    });
    
    function calcSyncFactor()
    {
        // calculate a factor for scroll areas with different height
        element2scrollSyncFactor = (element2.scrollHeight - element2.clientHeight) / (element1.scrollHeight - element1.clientHeight);    
    }
    
  • 在手指移动时更新滚动位置:

    document.addEventListener('touchmove', function() {
        for ( var i = 0; i < event.changedTouches.length; i++ ) {
            var touch = event.changedTouches[i];
    
            if ( touch === activeTouch ) {
                var yOffset = touch.screenY - touchStartY;
                element1.scrollTop = element1StartScrollTop + (0 - yOffset);
                syncScroll();
                break;
            }
        }    
    });
    
    function syncScroll()
    {
        element2.scrollTop = Math.round(element1.scrollTop * element2scrollSyncFactor);
    }
    
  • 当用户完成滚动并抬起手指时,应用惯性

    function applyInertia()
    {
        var velocity = offsetY - lastOffsetY;
        var time = Math.abs(velocity) / 150;
        var element1EndPosition = element1.offsetTop + velocity;
    
        element1.style.webkitTransition = 'top ' + time + 's ease-out';
        element1.style.top = element1EndPosition + 'px';
    
        element2.style.webkitTransition = 'top ' + time + 's ease-out';
        element2.style.top = Math.round(element1EndPosition * element2scrollSyncFactor) + 'px';
    }
    
    惯性是根据用户抬起手指时的速度计算出来的。对这些值进行修改以获得所需的结果。此函数中还可以实现橡皮筋效果。没有javascript参与应用css动画可能是一个技巧。另一种方法是在转换完成时注册事件。如果转换完成且滚动位置在容器外部,则应用一个新的转换,使内容返回动画


  • 看。请参阅。

    它们必须是2个div吗?你能分享一点HTML吗?@jn如果他们不共享相同的HTML,那么高度可能相差0-100px。这就是为什么我要计算滚动百分比并相应地滚动。你能做一个简单的演示让我们编辑吗?@Zachsauier更新了最后两种方法的小提琴,并将发布其他方法。所以方法3只是iOS的问题?你需要香草还是jQuery?@谢谢你的回答。但是,当快速移动手指并试图设置scrollTop时,会出现问题吗?而且它们的滚动高度也不一样,所以需要计算滚动的百分比。你的意思是:“快速移动手指并尝试设置滚动顶部”。你查过电话号码了吗。你可以随心所欲地快速移动手指。所有的浏览器滚动都必须被禁用。我的意思是说弹性滚动。当手指移动得更快时,我遇到了迟钝的问题,即触摸移动的速度。用css设置惯性的动画。这将获得最平滑的结果:通过比较上一次touchMove yOffset与之前的一次,计算滚动的速度。从这个速度应用一个动画(例如css转换),慢慢停止滚动这是我见过的最好的解释之一。
    var element1 = document.getElementById('content1');
    var element2 = document.getElementById('content2');
    
    var activeTouch = null;
    var touchStartY = 0;
    var element1StartScrollTop = 0;
    var element2scrollSyncFactor = 0;
    var offsetY = 0;
    var lastOffsetY = 0;
    
    document.addEventListener('touchstart', function(event) {
        event.preventDefault();
    
        var touch = event.changedTouches[0];
    
        if ( activeTouch == null ) {
            activeTouch = touch;
            touchStartY = touch.screenY;
            // use offsetTop instead of scrollTop
            element1StartScrollTop = element1.offsetTop;
            // if scroll content does not change do this calculation only once to safe compute time while animating
            calcSyncFactor();
    
            // cancel inertia animations
            element1.style.webkitTransition = 'none';
            element2.style.webkitTransition = 'none';
        }
    });
    
    function calcSyncFactor()
    {
        // calculate a factor for scroll areas with different height   
        // use the div's sizes instead of scrollTop
        element2scrollSyncFactor = (element2.clientHeight - element2.parentNode.clientHeight) / (element1.clientHeight - element1.parentNode.clientHeight);    
    }
    
    document.addEventListener('touchmove', function() {
        for ( var i = 0; i < event.changedTouches.length; i++ ) {
            var touch = event.changedTouches[i];
    
            if ( touch === activeTouch ) {
                lastOffsetY = offsetY;
                offsetY = touch.screenY - touchStartY;
                // use offsetTop instead of scrollTop
                element1.style.top = (element1StartScrollTop + offsetY) + 'px';
                syncScroll();
                break;
            }
        }    
    });
    
    function syncScroll()
    {
        element2.style.top = Math.round(element1.offsetTop * element2scrollSyncFactor) + 'px';
    }
    
    document.addEventListener('touchend', touchEnd);
    document.addEventListener('touchcancel', touchEnd);
    
    function touchEnd(event)
    {
        for ( var i = 0; i < event.changedTouches.length; i++ ) {
            var touch = event.changedTouches[i];
            if ( touch === activeTouch ) {
                applyInertia();
                activeTouch = null;
                break;
            }
        }    
    }
    
    function applyInertia()
    {
        var velocity = offsetY - lastOffsetY;
        var time = Math.abs(velocity) / 150;
        var element1EndPosition = element1.offsetTop + velocity;
    
        element1.style.webkitTransition = 'top ' + time + 's ease-out';
        element1.style.top = element1EndPosition + 'px';
    
        element2.style.webkitTransition = 'top ' + time + 's ease-out';
        element2.style.top = Math.round(element1EndPosition * element2scrollSyncFactor) + 'px';
    }