[英]Single page webapp screen transitions with maintaining the scroll position
我正在构建一个用于手机的单页Web应用程序。 应用程序应该实现“屏幕”之间的转换(像任何其他移动应用程序,例如Facebook,Twitter),这些转换应该是动画的(向左滑动)。 每个屏幕必须保持其在过渡之间的滚动位置。
一个明显的解决方案是:
Viewport
+----------+
|+--------+| +--------+ +--------+ +--------+
|| DIV1 || | DIV2 | | DIV3 | | DIV4 |
|| || | | | | | |
|| || | | | | | |
|| || | | | | | |
|| || | | | | | |
|+--------+| +--------+ +--------+ +--------+
+----------+
将不同的屏幕放入容器(DIV1,DIV2,......)中,这些容器的样式适合屏幕( position: absolute; width: 100%; height: 100%; top: 0
)并具有overflow-x: scroll
。 容器彼此相邻,过渡就像动画left
属性一样容易。
容易到目前为止。
问题如下:在此实现中,当用户向下滚动时,地址栏不会在移动浏览器中消失。
我在谈论这个功能:
这是因为移动浏览器做到这一点,只有当用户滚动body
-不是在一个容器body
。 有几个解决方案的建议 ,但它们不适用于所有目标平台(Android Chrome和本机浏览器,iPhone Safari),并且非常hacky。 我想保留浏览器的原始行为。
出于这个原因 - 显然 - 需要将滚动留在身体上。 这意味着容器必须是“全长”(而不是溢出滚动),仍然彼此相邻。 如果你想一想,这就是过渡变得困难的地方。
从DIV1转换到DIV2时,我当前的解决方案有以下步骤:
top
定位到窗口的当前scrollTop
left
属性,以便DIV2填充屏幕 top
移动到0
,这样用户就无法再滚动回到此屏幕的顶部 相反,过渡回DIV1是类似的。 这实际上工作得很好(虽然它非常复杂并且使用转换事件监听器)但不幸的是在iOS Safari下步骤3和4之间存在非常难看的闪烁效果,因为它在步骤3之后立即呈现页面而不等待第4步。
我正在寻找一个独立于框架( vanilla JS )的解决方案。
如果加载了jquery,你可以这样做
$(document).ready(function() {
if (navigator.userAgent.match(/Android/i)) {
window.scrollTo(0,0); // reset in case prev not scrolled
var nPageH = $(document).height();
var nViewH = window.outerHeight;
if (nViewH > nPageH) {
nViewH -= 250;
$('BODY').css('height',nViewH + 'px');
}
window.scrollTo(0,1);
}
});
对于Iphone,你必须做以下链接中提到的事情
http://matt.might.net/articles/how-to-native-iphone-ipad-apps-in-javascript/
和野生动物园
希望它能以某种方式帮助你!!
你的方法是对的。 由于滚动更改位置,您可能会得到闪烁。 诀窍是将div更改为position: fixed
滚动时position: fixed
,然后再将其更改回来。
步骤是:
position: fixed
scrollTop
更改为0 - scrollPosition
过渡后:
scrollTo()
更改窗口的滚动位置 position: fixed
在div上,因此自然的浏览器行为起作用。 这里是一个简单的vanilla javascript示例(也作为小提琴 ):
<!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上都有此事件,则该事件将触发两次。 解决方案是:
transitionEnd
事件,请使用requestAnimationFrame()
并通过js手动设置动画 在本例中,我还使用固定高度的容器进行过渡。 如果您的div具有不同的高度,则必须在转换后更改容器高度。 理想情况下,从position: fixed
恢复之前position: fixed
。
请记住,将div更改为position: fixed
将显示它,即使它位于具有overflow: hidden
的容器中。 在移动webapp的情况下,这不会是一个问题,因为div是在屏幕之外。 在PC上你可能不得不将另一个div放在另一个div上以隐藏转换的那个。
使用window.scrollTo(0,1);
你可以让导航栏消失。 这是一个黑客,但它的工作原理。
为什么不:
<body>
<div id=header>Header</div>
<div id=content>Scrollable Content</div>
<div id=footer>Footer</div>
</body>
然后CSS:
#header,#footer,#content{
left:0%;
right:0%;
}
#header,#footer{
position:fixed;
}
#header{
top:0%;
height:30px;
}
#footer{
bottom:0%;
height:30px;
}
#content{
position:absolute;
top:30px;
height:1000px; /*Whatever you need it to be*/
}
触摸屏响应<body>
标签而不是<div>
,因此设置position:fixed
在#header
和#footer
允许它们保持相对于窗口的位置,无论滚动位置如何,然后当用户滚动时内容,他们滚动<body>
编辑:我已经实现了这个例子:
访问您的移动设备。
我想我弄清楚了,这很棘手。
简而言之:在我提出的问题中,我描述了当前在iOS中闪烁的解决方案。 在第3点,你需要添加position: fixed
到DIV2。 这样它就会“坚持”并且你会避免在第4点闪烁。然后你需要延迟4点几毫秒( setTimeout
,500ms对我有效,但可能更小)并且设置position: absolute
再次到DIV2 window.scrollTo
。 我不确定你是否需要延迟,但没有它,屏幕仍然会闪烁。
如果有兴趣,我可以稍后发布PoC。
作为旁注,我发现,如果回答的人没有完全阅读问题或者完全忽略某些标准(框架独立性,保持原始滚动行为),那么大多数人都非常失望。 他们中的大多数建议我已经在问题中明确指出的解决方案是不可接受的。 有些人甚至在被投票时收回。
编辑:在我发布解决方案前几分钟, dreamlab回答了问题。 两种解决方案都使用position: fixed
。 他的解决方案也更加详细。 他值得赏金。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.