我正在为手机构建一个单页 Web 应用程序。应用程序应该实现“屏幕”之间的转换(就像任何其他移动应用程序,例如 Facebook、Twitter),并且这些转换应该是动画的(左右滑动)。每个屏幕都必须在转换之间保留其滚动位置。
想到的一个明显的解决方案是:
Viewport
+----------+
|+--------+| +--------+ +--------+ +--------+
|| DIV1 || | DIV2 | | DIV3 | | DIV4 |
|| || | | | | | |
|| || | | | | | |
|| || | | | | | |
|| || | | | | | |
|+--------+| +--------+ +--------+ +--------+
+----------+
不同的屏幕被放入容器(DIV1、DIV2、...)中,容器的样式适合屏幕(position: absolute; width: 100%; height: 100%; top: 0
) 并且有 overflow-x: scroll
。容器彼此相邻放置,转换就像为它们的 left
属性设置动画一样简单。
到目前为止很简单。
问题如下:在此实现中,当用户向下滚动时,地址栏不会在移动浏览器中消失。
我说的是这个功能:
这是因为移动浏览器只有在用户滚动 body
时才会执行此操作 - 而不是 body
中的容器。有several suggestions用于解决方案,但它们并不适用于所有目标平台(Android Chrome 和 native 浏览器,iPhone Safari)并且非常笨拙。我想保留浏览器的原始行为。
出于这个原因 - 显然 - 需要将滚动留在 body 上。这意味着容器必须是“全长”(而不是溢出滚动),仍然彼此相邻放置。如果您考虑一下,这就是过渡变得困难的地方。
从 DIV1 转换到 DIV2 时,我当前的解决方案具有以下步骤:
- 将DIV2的
top
定位到窗口的当前scrollTop
- 为 DIV1 和 DIV2 的
left
属性设置动画,使 DIV2 充满屏幕 - 在动画结束后将 DIV2 的
top
移到0
,这样用户就不能向后滚动到屏幕顶部以外的地方 - 将窗口的scrollTop移动到0
- 隐藏 DIV1(以防它比 DIV2 长)
返回 DIV1 的过程与此类似,只是相反。这实际上工作得很好(尽管它非常复杂并且使用了转换事件监听器)但不幸的是在 iOS Safari 下的第 3 步和第 4 步之间有一个非常丑陋的闪烁效果,因为它在第 3 步之后立即呈现页面而不等待第 4 步。
我正在寻找独立于框架的 ( vanilla JS ) 解决方案。
最佳答案
你的做法很对。由于滚动更改位置,您可能会出现闪烁。诀窍是在滚动时将 div 更改为 position: fixed
,然后再将它们改回来。
步骤是:
- 保存当前滚动的垂直位置
- 将 div 更改为
position: fixed
- 将 div 的
scrollTop
更改为0 - scrollPosition
- 开始横向过渡
转换后:
- 使用
scrollTo()
更改窗口的滚动位置 - 还原 div 上的
position: fixed
,以便浏览器的自然行为正常工作。
这是一个普通的 javascript 示例(也作为 fiddle ):
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title></title>
<style type="text/css">
body {
margin: 0;
padding: 0;
}
.container {
position: absolute;
overflow: hidden;
width: 320px;
height: 5000px;
}
.screen {
position: absolute;
width: 320px;
height: 5000px;
transition: left 0.5s;
}
#screen1 {
background: linear-gradient(red, yellow);
}
#screen2 {
left: 320px;
background: linear-gradient(green, blue);
}
#button {
position: fixed;
left: 20px;
top: 20px;
width: 100px;
height: 50px;
background-color: white;
color: black;
}
</style>
</head>
<body>
<div class="container">
<div id="screen1" class="screen"></div>
<div id="screen2" class="screen"></div>
</div>
<div id="button">transition</div>
<script type="text/javascript">
var screenActive = 1;
var screen1 = document.getElementById('screen1');
var screen2 = document.getElementById('screen2');
var screen1ScrollTop = 0;
var screen2ScrollTop = 0;
function onClick()
{
console.log('getting the event');
if ( screenActive === 1 ) {
// will show screen 2
screen1ScrollTop = document.body.scrollTop;
screen1.style.position = 'fixed';
screen2.style.position = 'fixed';
screen1.style.top = (0 - screen1ScrollTop) + 'px';
screen2.style.top = (0 - screen2ScrollTop) + 'px';
screenActive = 2;
screen1.style.left = '-320px';
screen2.style.left = '0px';
}
else {
// will show screen 1
screen2ScrollTop = document.body.scrollTop;
screen1.style.position = 'fixed';
screen2.style.position = 'fixed';
screen1.style.top = (0 - screen1ScrollTop) + 'px';
screen2.style.top = (0 - screen2ScrollTop) + 'px';
screenActive = 1;
screen1.style.left = '0px';
screen2.style.left = '320px';
}
}
function onTransitionEnd(event)
{
if ( screenActive === 1 ) {
window.scrollTo(0, screen1ScrollTop);
}
else {
window.scrollTo(0, screen2ScrollTop);
}
screen1.style.position = 'absolute';
screen1.style.top = '0px';
screen2.style.position = 'absolute';
screen2.style.top = '0px';
}
screen1.addEventListener('webkitTransitionEnd', onTransitionEnd);
document.getElementById('button').addEventListener('click', onClick);
</script>
</body>
</html>
在这个例子中,我使用了 transitionEnd
事件。请记住,如果您在两个动画 div 上都有此事件,则该事件将触发两次。对此的解决方案是:
- 如果时间相同,只需在一个 div 上使用事件(在示例中使用)
- 使用事件的所有 div,但只对事件的 div 进行相应的更改
- 为一个包含所有 div 的容器制作动画。所以你只需要一个事件。
- 如果您不能使用
transitionEnd
事件,请使用requestAnimationFrame()
并通过js 手动设置动画
在此示例中,我还使用固定高度的容器进行过渡。如果你有不同高度的 div,你将不得不在转换后改变容器的高度。理想情况下,在从 position: fixed
恢复之前。
请记住,将 div 更改为 position: fixed
将显示它,即使它位于带有 overflow: hidden
的容器中也是如此。对于移动网络应用程序,这不会成为问题,因为 div 在屏幕之外。在个人电脑上,您可能必须将另一个 div 放在另一个之上以隐藏正在过渡的那个。
关于javascript - 保持滚动位置的单页 webapp 屏幕转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25272020/