如何强制 Safari 重新绘制位置:滚动上的固定元素?

Posted

技术标签:

【中文标题】如何强制 Safari 重新绘制位置:滚动上的固定元素?【英文标题】:How to force Safari to repaint position:fixed elements on scroll? 【发布时间】:2014-05-15 14:21:07 【问题描述】:

我在桌面和移动设备上使用 Safari 时遇到问题,当用户滚动时,使用 position:fixed 重绘元素非常慢。

safari 难以处理的 position:fixed 元素是#intro 和 .portfolio-item .expanded-content 的页脚元素。滚动时的#intro 不一定会重新绘制到正确的 z-index(它应该在用户滚动时位于其他元素的后面)。移动设备上的页脚元素不会停留在 ios Safari 上滚动内容上方的固定位置。在 iOS safari 上滚动是锯齿状的(然而,iOS chrome 是流畅的,一切都按预期工作)。

我做了一个小提琴,去掉了所有的图像、字体和 JS,你瞧,safari 在重新绘制 position:fixed elements on scroll 时没有问题。

由于这是一个投资组合网站,因此删除我的图片显然不是一种选择。我真的希望把它变成一个真正的单页网站,而不是使用 AJAX 或任何东西来按需加载内容。我是否对 safari 要求太多才能拥有这么多元素并能够使用 position:fixed on scroll 重新绘制元素? Chrome和FF似乎没有问题; IE9、10、11 也不行。

我不完全确定这是重绘问题,但您可以在下面的视频中看到,如果我通过触发非滚动事件(如鼠标悬停事件)来强制 Safari 重绘,它会重绘并放置该位置:fixed 元素在我在样式表中指定的 z-index 中。所以这个事实,再加上小提琴工作得很好,这就是为什么我假设这是一个重绘问题,而不是我的代码的问题。

我希望找到一种方法来解决这个问题,而无需求助于更多的 JS 或动态加载的内容,以保持相同的设计(不要仅仅因为一个浏览器是而放弃使用 position:fixed 或流体布局的想法有问题)并尝试保持性能快速流畅。每次用户滚动以强制 safari 重新绘制时,我都考虑过使用 JS,但在我看来,这似乎会对所有浏览器的性能产生负面影响。我真的可以借鉴其他人的想法和观点。

网站:http://sarahjean.co

小提琴: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>

CSS:

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(3G 上的 iOS 6)、Chrome 33(桌面)、Chrome iOS 中测试。当我测试时,“设计/开发服务”框正在显示。 谢谢。我正在桌面版 Safari 7.0.3 上对其进行测试,但仍然遇到问题。我已经问了几个我认识的人在他们的环境中尝试它,他们也遇到了我的问题。也许我应该看看 6.1.2 和 7.0.3 之间的区别是什么?编辑:我刚刚在我丈夫的电脑上测试了 6.1.2,我也遇到了同样的问题。 我在 safari 5.17 上累了我找不到问题... 我刚刚登录到我的跨浏览器测试帐户并在 safari 6.1 上的 OSX 10.7 和 10.8 上进行了尝试,我能够在那里复制问题,所以我不知道为什么你们两个当我在我可以访问的每个 Safari 测试环境中看到它时,我无法复制它。我们滚动的方式有什么不同吗?您是否在滑动/使用鼠标滚轮?单击滚动条?你只是点击导航吗?滚动时您的光标是否停留在页面上的其他元素上,这可能会触发鼠标悬停事件? 【参考方案1】:

你没疯。我遇到了position: fixed 元素也没有重新绘制的问题。还没找到解决办法。

编辑找到了解决方案。你几乎可以通过触发一个 CSS 动画来重绘任何东西,从而改变它的大小。这是我正在使用的 sn-p:

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

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

【讨论】:

你能详细解释一下css样式吗? “&.active”是什么意思? 抱歉,是手写笔。这意味着 .foo.active 哇,优雅而简单的解决方案。我有一个相当复杂的菜单栏,在 iOS 上,当 scrollTop 需要删除固定类时我遇到了问题。这种方法完全解决了我的问题。 这个解决方案有什么副作用? 我真的不知道。我已经有一段时间没有使用或需要这个了。【参考方案2】:

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

在大多数情况下,延长动画执行时间对我来说很有效。

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


.repaint 
  animation: repaint 5000ms;

但是,如果固定位置的 DOM 元素在高度发生变化的父元素内(例如,当动态添加新的 DOM 元素时,父元素的高度会发生变化),那么它对我不起作用,即使将动画时间延长到不合理的值.

我的最终解决方案是放弃animation hack 并在 JS 中强制重绘

$('.repaint').hide().show(0);

如Force DOM redraw/refresh on Chrome/Mac中建议的那样

我使用 AngularJS,为了让这个 hack 在所有情况下都能正常工作,我必须在每个摘要循环中调用 .hide().show(0)

以AngularJS指令的形式破解:

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

【讨论】:

【参考方案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() 从我的标头中添加和删除类,所以在需要时,jquery 函数还向我的标头添加了 .repaint 类,它为我解决了这个问题。

【讨论】:

以上是关于如何强制 Safari 重新绘制位置:滚动上的固定元素?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS 上的 Safari 中滚动时监控滚动位置?

iOS 10 Safari:防止在固定覆盖层后面滚动并保持滚动位置

iOS 上的 Safari 仅在滚动停止后显示固定元素

单击 Safari 上的后退按钮时使用 React 挂钩强制页面重新加载

如何强制显示移动 Safari 底部导航栏以编程方式显示?

iOS 12 Safari iframe + 位置:固定 + translate3d 错误