我正在尝试同步两个可滚动的DIVS滚动位置。
遵循的方法:
Method - 1:滚动事件设置其他DIV的scrollTop。 问题:滚动事件在最后执行,UI在iOS游侠中很慢。
Method - 2:使用setInterval同步两个滚动位置。 问题:iOS在滚动期间不执行计时器功能, 所以滚动位置在最后同步。再次这更加缓慢。 试过,计时器修复如许多博客中提到的那样,但仍然没有优雅。
Method -3:尝试了自定义滚动条,因此iScroll尝试同步scroll
事件,
问题:这似乎更好,但在iOS仍然是缓慢!
Method -4:尝试了自定义滚动条,因此iScroll尝试同步scroll
事件,
问题:使用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);
如何使两个可滚动DIVS的同步滚动位置更加平滑。请给我一些建议,这让我感到恼火。
答案 0 :(得分:1)
依赖于滚动事件(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和之前的yOffset来计算滚动的速度。从此速度应用动画(例如css过渡),慢慢停止滚动
见FIDDLE。见result on iOS。小提琴只实现触摸设备的解决方案。对于桌面设备使用OP的方法1.实现一个条件,根据设备检查使用哪种方法。
可以使用requestAnimationFrame
在javascript中制作动画。在移动设备上可能更高效的方式可能是使用css转换或css动画。虽然元素滚动位置无法使用css进行动画处理。
将html的结构更改为。
div:overflow: hidden
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动画可能是诀窍。另一种方法是在转换完成时注册事件。如果转换完成且滚动位置在容器外部,则应用一个新的转换,将内容设置为动画。