我正在尝试同步两个可滚动的 DIVS 滚动位置。
遵循的方法:
Method - 1 : on-scroll事件设置其他DIV的scrollTop。 问题:滚动事件最后执行,UI 在 iOS Safari 中缓慢。
Method - 2 :使用 setInterval 来同步两个滚动位置。 问题:iOS 在滚动期间不执行定时器功能, 所以滚动位置在最后同步。同样,这更加缓慢。 尝试过,许多博客中提到的计时器已修复,但仍然没有优雅。
Method -3 :尝试自定义滚动条,所以 iScroll 并尝试在 scroll
事件上同步两者,
问题:这看起来好多了,但在 iOS 中仍然很慢!!!
Method -4 :尝试自定义滚动条,所以 iScroll 并尝试在 scroll
事件上同步两者,
问题:使用 iScroll 但使用定时器而不是依赖于 onScroll 事件,
但是在touchmove期间,iOS忙于提供动画
而是执行所需的计时器直到触摸结束。
下面的代码引用了这个方法。它也很迟钝。
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);
如何使两个可滚动 DIVS 的同步滚动位置更顺畅。请给我一些建议,这让我很恼火。
最佳答案
依靠滚动事件(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); }
可以添加一个仅在用户将手指移动了一些像素后才开始滚动的检查。这样,如果用户单击某个元素,文档将不会滚动一些像素。
用户抬起手指后的清理:
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 ) { // calculate inertia and apply animation activeTouch = null; break; } } }
要使滚动感觉更自然,请应用 iOS 橡皮筋效果和惯性。通过将最后一个
touchMove
yOffset 与之前的比较来计算滚动速度。从这个速度应用一个动画(例如 css 过渡)来慢慢停止滚动
参见 FIDDLE .见result on iOS . fiddle 只实现了触摸设备的解决方案。对于桌面设备,使用 OP 的方法 1. 实现一个条件,检查根据设备使用哪种方法。
如何通过 css 转换应用惯性
可以使用 requestAnimationFrame
在 javascript 中制作动画。在移动设备上可能更高效的方法是使用 css 转换或 css 动画。虽然元素滚动位置不能用 css 动画。
将 html 的结构更改为。
div:
的容器溢出:隐藏
div:
的内容position: absolute
根据内容大小,在内容 div 上使用 css 属性
-webkit-transform: translateZ(0)
。这将创建一个具有自己的背衬表面的新层,该层将在 gpu 上合成。
实现上述函数,以便它们为内容的
top
位置而不是scrollTop
设置动画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'; }
惯性是根据用户抬起手指时的速度计算得出的。 摆弄这些值以获得所需的结果。橡皮筋效果也可以在此函数中实现。没有 javascript 涉及应用 css 动画可能是诀窍。另一种方法是为转换完成时注册事件。如果过渡完成并且滚动位置在容器外部,则应用一个新的过渡来动画内容。
参见 FIDDLE .见result on iOS .
关于javascript - 如何使同步两个 Divs 滚动位置更平滑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25477651/