如何在惯性滚动期间同步两个元素的滚动偏移

时间:2013-03-08 22:41:10

标签: ipad scroll mobile-safari touchmove inertia

我需要保持元素的滚动偏移与另一个元素(实际上是窗口)保持同步,而且我在移动Safari(iPad)上滚动的惯性“滚动”阶段遇到了麻烦。

我有几个带position:fixed; overflow:hidden的div,我需要保持它们的滚动偏移与窗口的一个同步(意味着整个身体滚动。)通常我会像这样(jQuery)编码:< / p>

var $win = $(window),
    $div1 = $(...)

$win.scroll(function() { 
    $div1.scrollTop($win.scrollTop())
})

但是在iPad上测试界面时,我注意到在触摸阶段,当你用手指拖动虚拟页面时,或者在惯性阶段,当你放开页面时,div都没有更新放慢速度。

我通过注册touchmove事件的处理程序以及scroll事件来解决拖动阶段。

但我无法找到解决惯性阶段问题的方法。 div保持静止(并且慢慢地与页面的其余部分不同步),直到惯性运动完全停止,当滚动事件最终被触发并且它跳到位置时。

Here's a working demo.

尝试在iPad上滚动以查看“惯性晃动”问题。不幸的是,由于iPad在iframe滚动时的奇怪行为,我无法让它在jsFiddle上工作。

如果我可以在该阶段进行轮询,我可以保持两个元素之间的同步。我尝试使用setTimeoutsetIntervalrequestAnimationFrame,但在惯性滚动阶段都没有触发。似乎所有Javascript都在此阶段停止。

问题:

  • 在惯性滚动阶段是否有触发或滚动事件?
  • 在该阶段是否有任何方式运行Javascript回调?
  • 有没有办法使用CSS或JS以外的其他技术同步两个元素(X或Y,而不是两者)的滚动偏移?

3 个答案:

答案 0 :(得分:4)

iOS实际上会在惯性滚动发生时冻结DOM操作 Javascript。下面是一个简单的演示,用于说明在普通桌面环境中滚动与iPad之间的区别:http://jsfiddle.net/notjoelshapiro/LUcR6/。这段代码中只有这个JS:

var num = 0;
function updateNum(){
    num++;
    $('#awesomeDiv').text(num);
}
$(window).scroll(updateNum);

在滚动时,它会增加一个数字并将其显示在页面底部。您将看到屏幕底部的滚动编号仅在惯性滚动停止时递增。如果Javascript在后台运行,它将不会刷新,直到滚动结束但数字应该增加更高。

所以要具体回答你的问题:

  

在惯性滚动阶段是否有触发或滚动事件?

否定,ghostrider。

  

在该阶段有没有办法运行Javascript回调?

见上文。

  

有没有办法使用CSS或JS以外的其他技术同步两个元素(X或Y,而不是两者)的滚动偏移?

不太清楚你在这里的意思,JS可以完成你需要的所有数学运算,但必须在惯性滚动完成时。说实话,这可能是tl; dr的症状,因为我现在在工作。

你看过iScroll图书馆吗?它模拟触摸和非触摸环境的惯性(或非惯性)滚动,并在“滚动”和“惯性滚动”期间/之后为您提供JS回调,并提供大量信息,您可以使用它们来计算您在页。

答案 1 :(得分:4)

OldDrunkenSailor打败了我,建议iScroll

不幸的是,开箱即用的iScroll只是复制了与原生惯性滚动相同的问题 - 在惯性阶段没有事件处理。

这是一个带有猴子修补的iScroll的演示版本,用于添加即使在惯性阶段也会触发的自定义事件:https://dl.dropbox.com/u/15943645/scrollingdemo.html

在我的第二代iPad上运行得很好。

JS:

// Disable touch events
document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);

// Patch iScroll for position change custom event
iScroll.prototype._oldPos = iScroll.prototype._pos;
iScroll.prototype._pos = function(x, y) {
    this._oldPos(x, y);
    if (this.options.onPositionChange) this.options.onPositionChange.call(this);
}

$(function() {
    var $win = $(window),
        $div_cols = $('#cols'),
        $div_rows = $('#rows'),
        $div_body = $('#body')

    // attach scrolling sync handler and execute it once
    function sync_scroll(e) {
        $div_cols.scrollLeft(0 - $div_body.position().left);
        $div_rows.scrollTop(0 - $div_body.position().top);

    }           

    // initialize iScroll on wrapper div, with position change handler
    var myScroll = new iScroll('iscroll_wrapper', {
        bounce: false,
        onPositionChange: sync_scroll
    });
})

CSS:

#iscroll_wrapper {
    position:absolute;
    z-index: 1;
    left: 168px; 
    top:77px; 
    bottom:0px; 
    right:0;
    overflow:auto;
}

#body {
    position:absolute;
    z-index: 1;
    width: 2046px; 
    height: 3376px;

}

请注意,只有主体响应触摸事件,但您可以将技术扩展到行和cols div以获得反向关系。

答案 2 :(得分:0)

  

滚动开始时触发。请注意,iOS设备在滚动期间冻结DOM操作,将它们排队以在滚动完成时应用。我们目前正在研究在滚动开始之前允许应用DOM操作的方法。

http://jquerymobile.com/demos/1.0rc1/docs/api/events.html#/demos/1.0rc1/docs/api/events.html

这就是他们在滚动开始部分所说的话