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

7
我需要将一个元素的滚动偏移量与另一个元素(实际上是窗口)保持同步,但在移动Safari(iPad)上惯性“滚动停止”阶段时,我遇到了麻烦。
我有几个带有position:fixed; overflow:hidden的div,并且我需要将它们的滚动偏移量与窗口的滚动偏移量保持同步(即整个body滚动)。通常我会这样编码(使用jQuery):
var $win = $(window),
    $div1 = $(...)

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

但是,在iPad上测试接口时,我注意到在触摸阶段(当您用手指拖动虚拟页面时)和惯性阶段(当您松开并且页面减速停止时),div都没有被更新。我通过注册touchmove事件和scroll事件的处理程序来解决拖动阶段的问题。但是我找不到解决惯性阶段问题的方法。div保持不动(并缓慢与页面的其余部分失去同步),直到惯性运动完全停止,当滚动事件最终被触发并跳到位置时。这里有一个可工作的演示。在iPad上尝试滚动以查看“惯性滚动”问题。不幸的是,由于iPad在iframe滚动方面的奇怪行为,我无法让它在jsFiddle上运行。
如果我能在那个阶段进行轮询,就可以在两个元素之间保持一定的同步。我已经尝试过使用setTimeout、setInterval和requestAnimationFrame,但它们都不会在惯性滚动阶段触发。似乎在这个阶段所有的Javascript都停止了。
问题:
- 在惯性滚动阶段是否有任何触摸或滚动事件被触发? - 是否有任何方法在那个阶段运行Javascript回调函数? - 是否有一种方式可以使用CSS或其他技术(而非JS)来同步两个元素的滚动偏移量(只限于X或Y轴)?

将div元素作为子元素添加到滚动层中是否实用? - Four_lo
“滚动层”是什么意思?被滚动的元素是窗口,也就是整个页面主体。固定的div在主体内部,但它们有position:fixed属性。 - Tobia
我基本上是根据这里获取的信息进行操作的:http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/UIScrollView_pg/CreatingBasicScrollViews/CreatingBasicScrollViews.html#//apple_ref/doc/uid/TP40008179-CH101-SW1 - Four_lo
我已经添加了一个可工作的演示,可以重现这个问题。请忽略大量的HTML和CSS。如果您想查看元素结构和它们的CSS,可以使用浏览器的检查器,但实际上,理解问题所需的只有我的问题中表达的内容。谢谢。 - Tobia
3个回答

4

iOS在惯性滚动时实际上会冻结DOM操作和Javascript。这是我制作的一个简单演示,用于说明在普通桌面环境与iPad上滚动之间的差异:http://jsfiddle.net/notjoelshapiro/LUcR6/。此代码仅包含以下JS:

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

在滚动时,它会递增一个数字并将其显示在页面底部。您会发现屏幕底部的滚动数字仅在惯性滚动停止时才递增。如果Javascript在后台运行,它不会在滚动结束之前刷新,但数字应该递增更高。
因此,具体回答您的问题:
有没有在惯性滚动阶段触发任何触摸或滚动事件?
否定的,幽灵骑士。
在那个阶段有没有办法运行Javascript回调函数?
见上文。
有没有一种方法可以使用CSS或其他技术(而不是JS)同步两个元素的滚动偏移量(X或Y中的任一个)?
不太确定您的意思,JS可以完成您需要的所有数学计算,但必须在惯性滚动完成后进行。老实说,这可能是因为我现在正在工作,所以这可能是tl;dr的症状。

你看过iScroll库了吗?它模拟触摸和非触摸环境的惯性(或非惯性)滚动,并在“滚动”和“惯性滚动”期间/之后提供JS回调,并提供了大量信息,可用于计算页面上的位置。


感谢您的研究。我没有想到完全用库替换本地滚动,但这是一个解决方法,非常合理。我希望把奖励给Nick的工作演示是可以的。 - Tobia

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;

}

请注意,只有body元素会响应触摸事件,但您可以将此技术扩展到行和列的div元素以获得反向关系。


我之前没有想过要完全用一个库来替换本地滚动,但这是一个很好的解决方法。感谢@OldDrunkenSailor和Nick。奖励Nick的工作演示! - Tobia

0

谢谢,但那个修复与我的问题无关。这不是硬件加速的错误。没有元素消失。这是一个关于JavaScript事件在自动“惯性”滚动阶段未被触发的问题。 - Tobia
哦,我以为它们只是滞后了。 - Four_lo
你的 win.scroll 调用完整吗? - Four_lo
这是一个非常简单的调用。完整的代码以这种方式同步了几个div。我担心文档引用没有起作用。 - Tobia
好吧,祝你好运。我只知道滚动可能会在事件处理程序中快速触发,可能会占用主要位置,所以我认为它需要一个中断。祝你好运。 - Four_lo

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