最近、ラバーバンディングがエクスペリエンスを損なうSPAで同じ問題<body>
に遭遇しましたが、サブエリアでスクロールする必要がありました. 方法 1 が私にとって最も効果的だったので、dSquared の提案に感謝します。.scroll
これは、クラス
を持っている要素 (div だけでなく) を見つけるためにツリーをずっと検索するプロジェクトで実装した彼の提案を少し拡張したものです。
// Prevent rubber-banding of the body, but allow for scrolling elements
$('body').on('touchmove', function (e) {
var searchTerms = '.scroll, .scroll-y, .scroll-x',
$target = $(e.target),
parents = $target.parents(searchTerms);
if (parents.length || $target.hasClass(searchTerms)) {
// ignore as we want the scroll to happen
// (This is where we may need to check if at limit)
} else {
e.preventDefault();
}
});
CSS は次のようになります。
body {
height: 100%;
overflow: hidden;
}
.scroll, .scroll-y, .scroll-x {
-webkit-overflow-scrolling: touch;
}
.scroll > *, .scroll-y > *, .scroll-x > * {
-webkit-transform : translateZ(0);
}
.scroll { overflow: auto; }
.scroll-y { overflow-y: auto; }
.scroll-x { overflow-x: auto; }
必要なライブラリは 1 つだけ (jQuery またはZepto ) で、勢いのあるネイティブ スクロールが得られ、本体にラバーバンディングはありません。また、translateZ を追加して、スクロール中に要素が消えるといういくつかの問題を修正しました。GPU を使用して要素を高速化することができます。
しかし (これは大きな問題ですが)、dSquared が指摘するように、スクロール要素が限界に達し、さらにスクロールしようとすると、ページ全体がラバーバンドになります。個人的には、これは失敗だと考えているので、引き続き取り組んでいます。OPのコードの行に沿ってチェックを追加することが答えかもしれませんが、私は試していません。
更新 (10/7/12):
多くの作業の後、次のコードが iOS6 で完全に動作するようになりました (他のコードではテストしていません)。本体にラバーバンディングがなく、スクロール領域の限界に達しても問題がなくなり、全体でネイティブのスクロール パフォーマンスが得られます。明らかに元のコードよりもはるかに多くのコードですが、これにより OP の目標に最も近い動作が得られると思います。
(function registerScrolling($) {
var prevTouchPosition = {},
scrollYClass = 'scroll-y',
scrollXClass = 'scroll-x',
searchTerms = '.' + scrollYClass + ', .' + scrollXClass;
$('body').on('touchstart', function (e) {
var $scroll = $(e.target).closest(searchTerms),
targetTouch = e.originalEvent.targetTouches[0];
// Store previous touch position if within a scroll element
prevTouchPosition = $scroll.length ? { x: targetTouch.pageX, y: targetTouch.pageY } : {};
});
$('body').on('touchmove', function (e) {
var $scroll = $(e.target).closest(searchTerms),
targetTouch = e.originalEvent.targetTouches[0];
if (prevTouchPosition && $scroll.length) {
// Set move helper and update previous touch position
var move = {
x: targetTouch.pageX - prevTouchPosition.x,
y: targetTouch.pageY - prevTouchPosition.y
};
prevTouchPosition = { x: targetTouch.pageX, y: targetTouch.pageY };
// Check for scroll-y or scroll-x classes
if ($scroll.hasClass(scrollYClass)) {
var scrollHeight = $scroll[0].scrollHeight,
outerHeight = $scroll.outerHeight(),
atUpperLimit = ($scroll.scrollTop() === 0),
atLowerLimit = (scrollHeight - $scroll.scrollTop() === outerHeight);
if (scrollHeight > outerHeight) {
// If at either limit move 1px away to allow normal scroll behavior on future moves,
// but stop propagation on this move to remove limit behavior bubbling up to body
if (move.y > 0 && atUpperLimit) {
$scroll.scrollTop(1);
e.stopPropagation();
} else if (move.y < 0 && atLowerLimit) {
$scroll.scrollTop($scroll.scrollTop() - 1);
e.stopPropagation();
}
// If only moving right or left, prevent bad scroll.
if(Math.abs(move.x) > 0 && Math.abs(move.y) < 3){
e.preventDefault()
}
// Normal scrolling behavior passes through
} else {
// No scrolling / adjustment when there is nothing to scroll
e.preventDefault();
}
} else if ($scroll.hasClass(scrollXClass)) {
var scrollWidth = $scroll[0].scrollWidth,
outerWidth = $scroll.outerWidth(),
atLeftLimit = $scroll.scrollLeft() === 0,
atRightLimit = scrollWidth - $scroll.scrollLeft() === outerWidth;
if (scrollWidth > outerWidth) {
if (move.x > 0 && atLeftLimit) {
$scroll.scrollLeft(1);
e.stopPropagation();
} else if (move.x < 0 && atRightLimit) {
$scroll.scrollLeft($scroll.scrollLeft() - 1);
e.stopPropagation();
}
// If only moving up or down, prevent bad scroll.
if(Math.abs(move.y) > 0 && Math.abs(move.x) < 3){
e.preventDefault();
}
// Normal scrolling behavior passes through
} else {
// No scrolling / adjustment when there is nothing to scroll
e.preventDefault();
}
}
} else {
// Prevent scrolling on non-scrolling elements
e.preventDefault();
}
});
})(jQuery);