如何强制Safari在滚动时重新渲染position:fixed元素?

16
我在桌面和移动设备上使用Safari时,发现当用户滚动时,使用position:fixed的元素重绘速度非常缓慢。
Safari难以处理position:fixed的元素,这些元素包括.portfolio-item .expanded-content的#intro和页脚元素。滚动时,#intro不一定会重绘到正确的z-index(它应该在用户滚动时位于其他元素后面)。在iOS Safari上,页脚元素在移动设备上不能保持固定位置。在iOS Chrome上滚动很流畅,一切都按预期工作。
我做了一个fiddle,把所有的图片、字体和JS都去掉了,结果发现Safari在滚动时没有问题重绘position:fixed元素。
因为这是一个作品集网站,删除我的图片显然不是一个选项。我真的希望将其变成一个真正的单页面网站,而不是使用AJAX或任何东西来按需加载内容。我是否要求Safari太多了,让它有那么多元素并能够在滚动时重绘position:fixed的元素?Chrome和FF似乎没有问题,IE9、10、11也没有问题。
我不完全确定这是否是重新绘制的问题,但您可以在下面的视频中看到,如果我通过触发一个非滚动事件(如mouseover事件)来强制Safari重新绘制,它会重新绘制,并将该position:fixed元素放置在我在样式表中指定的z-index中。因此,这个事实加上fiddle可以正常工作,这就是为什么我认为这是重新绘制的问题,而不是我的代码问题。
我希望找到一种解决这个问题的方法,而不需要使用更多的JS或动态加载内容,以保持相同的设计(不放弃使用position:fixed或流体布局的想法,只是因为一个浏览器有问题),并尝试保持快速和平滑的性能。我考虑过每次用户滚动时使用JS来强制Safari重新绘制,但我认为这似乎会对所有浏览器的性能产生负面影响。我真的需要其他人的想法和观点。
网站:http://sarahjean.co fiddle:http://jsfiddle.net/sjc8611/n9z3W/ 视频:https://dl.dropboxusercontent.com/u/24724104/position-fixed-repaint-lag-safari.mov HTML代码:
    <nav data-scroll-header="" id="main-navigation">
    <ul>
        <li><a href="#work" data-scroll="">Work</a>

        </li>
        <li><a href="#about" data-scroll="">About</a>

        </li>
        <li><a href="#services" data-scroll="">Services</a>

        </li>
        <li><a href="#contact" data-scroll="">Contact</a>

        </li>
    </ul>
</nav>
<div class="header" id="header">Header Content</div>
<section id="intro" class="container">
    <article class="content">
            <h1>Introduction Text</h1>

        <p>Welcome to my super cool portfolio site. Check out how awesome I am. You should totally hire me.</p>
    </article>
</section>
<section id="work" class="container">
    <article class="content">
            <h1>Work</h1>

        <nav id="portfolio-navigation">
            <ul>
                <li><a href="#work1">See My Work 1</a>

                </li>
                <li><a href="#work2">See My Work 2</a>

                </li>
            </ul>
        </nav>
    </article>
    <article id="work1" class="portfolio-item">
        <div class="expanded-content">
                <h2 class="center">Here is some of my work!</h2>

            <p>Lorem ipsum dolor sit amet..</p>
            <footer><a href="#work">Close</a>

            </footer>
        </div>
    </article>
    <article id="work2" class="portfolio-item">
        <div class="expanded-content">
                <h2 class="center">More of my cool work!</h2>

                <h1>Proin Quis Tortor Orci. Etiam At Risus</h1>

            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit..</p>
            <footer><a href="#work">Close</a>

            </footer>
        </div>
    </article>
</section>
<section id="contact" class="container">
    <article class="content">
            <h1>Contact</h1>

        <ul id="contact-list">
            <li>I would include a list of ways to contact me here</li>
            <li>Emails</li>
            <li>Telephones</li>
            <li>The postal services</li>
        </ul>
    </article>
</section>
<section id="services" class="container">
    <article class="content">
         <h1>Services</h1>
        <p>Lorem ipsum dolor sit amet..</p>
    </article>
</section>

样式表:

body {
    background: #fff8ec;
    margin: 0 auto;
    height: 100%;
}
html {
    font-family: Arial, sans-serif;
    font-size: 14px;
    line-height: 135%;
    color: #4b3d2f;
    height: 100%;
}
h1, h2, h3, h4, h5 {
    font-family: Arial, sans-serif;
}
h1 {
    color: #aba499;
    text-align: center;
    font-size: 2em;
}
.portfolio-item h2 {
    font-size: 1.8em;
}
a, a:link, a:visited {
    color: #c85128;
    text-decoration: none;
}
a:hover {
    color: #4b3d2f;
}
p {
    margin: 1em 0;
    line-height: 135%;
}
img {
    max-width: 100%;
    height: auto;
}
.container {
    width: 100%;
    position: relative;
    background-color: #e5e2de;
    padding: 100px 0;
}
.container > .content {
    width: 80%;
    margin: 0 auto;
    max-width: 800px;
    background-color: transparent;
}
#header {
    background-color: #c85128;
    height: 95%;
    position: relative;
    z-index: 3;
    display: table;
    width: 100%;
}
#intro {
    background-color: transparent;
    position: fixed;
    top: 5%;
    left: 0px;
    height: 25%;
    padding: 5% 0;
    z-index: 0;
}
#intro > .content {
    background-color: #fff8ec;
    width: 70%;
    padding: 5%;
    border-radius: 20px;
    box-shadow: 0px 1px 3px #e5e2de;
}
#work {
    margin-top: 55%;
    background-color: #dedad5;
}
#contact {
    background-color: #d8d3cd;
}
#services {
    background-color: #d1cbc4;
}
#about {
    background-color: #cac4bc;
}
section h1 {
    padding: 50px 0;
}
article .expanded-content h2, article .expanded-content p {
    margin: 50px auto;
}
#main-navigation {
    display: table;
    width: 100%;
    background-color: #aba499;
    position: fixed;
    top: 0;
    left: 0;
    height: 3em;
    overflow: visible;
    z-index: 2;
}
#main-navigation a {
    color: #fff8ec;
    font-family:'NovecentowideBookRegular', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    display: block;
}
#main-navigation a:hover {
    color: #4b3d2f;
    text-shadow: 0em -0.05em 0em #e5e2de;
}
#main-navigation ul {
    display: table-row;
    height: 3em;
    overflow: visible;
}
#main-navigation ul li {
    display: table-cell;
    width: 20%;
    padding: .8em;
    text-align: center;
    vertical-align: middle;
}
.portfolio-item {
    max-height: 0px;
    height: 0px;
    overflow: hidden;
    position: fixed;
    top: 3em;
    left: 0%;
    -webkit-transition: height 700ms ease;
    -moz-transition: height 700ms ease;
    -ms-transition: height 700ms ease;
    -o-transition: height 700ms ease;
    transition: height 700ms ease;
}
#work1:target, #work2:target {
    max-height: 1000px;
    opacity: 1;
    width: 80%;
    height: 70%;
    padding: 5%;
    top: 5%;
    left: 5%;
    background-color: #fff;
    box-shadow: 0px 0px 100px rgba(0, 0, 0, 0.5);
    z-index: 10;
    overflow-y: scroll;
    -webkit-overflow-scrolling: touch;
}
#work1:target .expanded-content, #work2:target .expanded-content {
    max-width: 900px;
    margin: 0 auto;
}
#work1:target .expanded-content footer, #work2:target .expanded-content footer {
    display: block;
    width: 90%;
    text-align: right;
    background-color: #c85128;
    position: fixed;
    top: 5%;
    left: 5%;
    z-index: 11;
}
#work1:target .expanded-content footer a, #work2:target .expanded-content footer a {
    display: block;
    padding: 1em;
    color: #e5e2de;
    height: 1em;
}

很想帮忙,但我在视频中没有看到你所描述的问题。在Safari(桌面)6.1.2、Safari Mobile(iOS 6 on 3G)、Chrome 33(桌面)、Chrome iOS上进行了测试。当我测试时,“设计/开发服务”框显示正常。 - cantera
谢谢。我正在桌面版Safari 7.0.3上测试它,仍然存在问题。我已经请几个我认识的人在他们的环境中尝试过,他们也遇到了和我一样的问题。也许我应该看看6.1.2和7.0.3之间的区别?编辑:我刚刚在我丈夫的电脑上测试了6.1.2,结果也遇到了同样的问题。 - Sarah Jean
我在Safari 5.17上尝试了它,但找不到问题... - T J
我刚刚登录了我的跨浏览器测试账户,并在Safari 6.1上的OSX 10.7和10.8上尝试了一下,我能够在那里复制这个问题,所以我不确定为什么你们两个无法复制它,而我在每个Safari测试环境中都看到了它。我们滚动的方式有区别吗?你们是在滑动/使用鼠标滚轮吗?点击滚动条?你们只是点击导航吗?当你们滚动时,你们的光标是否停留在页面上的其他元素上,可能会触发mouseover事件? - Sarah Jean
3个回答

12

你不是疯了。我也遇到了position: fixed元素不重新绘制的问题。目前还没有找到解决方案。

编辑:找到了解决方案。通过在元素上触发一个CSS动画来使几乎任何东西都能重绘,这会使元素的大小发生变化。以下是我使用的代码片段:

.foo
  position: fixed
  &.active
    animation: repaint 1ms

@keyframes repaint
  from
    width: 99.999%
  to
    width: 100%

你能详细解释一下CSS样式吗?"&.active"是什么意思? - malex
抱歉,这是Stylus。它的意思是.foo.active。 - corysimmons
哇,优雅而简单的解决方案。我有一个相当复杂的菜单栏,在iOS上,当scrollTop需要移除固定类时,我遇到了问题。这种方法完全解决了我的问题。 - Hooman Askari
这个解决方案有哪些副作用? - Gunchars
我不是很清楚。我已经有一段时间没有使用或需要这个了。 - corysimmons
在2021年,Safari仍然存在这个问题。但是您的解决方案解决了它。 - Johansrk

3

如果你不知道什么是触控笔样式,因此无法使用@CorySimmons的解决方案,这里提供了一个CSS版本的上述代码。同时,我不得不更改一些值,显然上述值在iOS 8中不起作用。

@-webkit-keyframes repaint {

    from {
        width: 99.9%;
    }
    to {
        width: 100%;
    }

}

@-moz-keyframes repaint {

    from {
        width: 99.9%;
    }
    to {
        width: 100%;
    }

}

@keyframes repaint {

    from {
        width: 99.9%;
    }
    to {
        width: 100%;
    }

}

.repaint {
    -webkit-animation: repaint 100ms;
       -moz-animation: repaint 100ms;
        -ms-animation: repaint 100ms;
            animation: repaint 100ms;
}

你只需要在需要重新绘制时给固定元素添加一个 .repaint 类即可。在我的情况下,我使用 jQuery 的 scrollTop() 函数来添加和删除我的 masthead 上的类,因此当需要时,jQuery 函数也会将 .repaint 类添加到我的 masthead 中,这对我解决了问题。

3

我在Safari 9.1中遇到了完全相同的问题。

延长动画执行的时间在大多数情况下对我有用。

@keyframes repaint {
  from {
    width: 99.999%
  }
  to {
    width: 100%
  }
}

.repaint {
  animation: repaint 5000ms;
}

然而,如果固定位置的DOM元素位于高度发生变化的父级容器内(例如,在动态添加新的DOM元素时),即使将动画时间延长到不合理的值,它对我也无效。
我的最终解决方案是放弃使用 animation 技巧,并在JS中强制重新绘制。
$('.repaint').hide().show(0);

正如在强制Chrome/Mac上DOM重绘/刷新中建议的那样,我使用AngularJS。为使此技巧在所有情况下都能正常工作,我必须在每个digest循环中调用.hide().show(0)

AngularJS指令形式的Hack:

function ForceRepaintDirective() {
  return {
    restrict: 'EA',
    link: function(scope, $element) {
      scope.$watch(function() {
        $element.hide().show(0);
      });
    }
  };
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接