Tips-移动端滑动固顶效果(position: sticky)

Posted on 2017-01-30 in CSS3 by yucongchen

先放个图看看滑动固顶是啥效果:

Tips-移动端滑动固顶效果。公众号:程序员的诗和远方

中间那个 tab 条,平常的时候是固定的,等到页面滑上去的时候,又像 fixed 一样贴在顶部。 position: sticky 就是用来实现这个效果的,元素不脱离文档流,仍然保留高度,所以这个属性真是人畜无害啊,而且效果如丝般润滑,堪比原生。

事实上,很多看起来人畜无害的东西,其背后都有一个大坑。

我们的 html 结构是这样的:

<body ontouchstart="">
    <div class="page-wrapper">
      <div id="contentA" class="content-a">
        A
      </div>
      <div class="sticky-wrap ">
        <ul class="ui-tab-nav ui-border-b frown ">
          <li class="current">推荐</li>
          <li>分类</li>
        </ul>
      </div>
      <div id="contentB" class="content-b">
        B
      </div>
    </div>
</body>

主要的 css 如下:

.sticky-wrap {
  top: 0;
  width: 100%;
  z-index: 999;
}
.sticky-wrap {
  position: relative;
  position: -webkit-sticky;
}
.page-wrapper.sticky .sticky-wrap {
  position: fixed;
}
.content-a {
  height: 200px;
  background-color: #12b7f5;
  color: #fff;
  font-size: 48px;
  text-align: center;
  line-height: 200px;
}
.content-b {
  height: 800px;
  font-size: 48px;
  text-align: center;
  line-height: 200px;
}

这里需要注意几点,这里 sticky-wrap 是我们设置了position: -webkit-sticky;的元素,这个元素的位置比较重要,不是随便放哪都可以实现那个效果的,sticky 效果是按照父元素的高度来的,如果你的父元素高度很小,会出现滑完父元素就不再固顶的奇怪情况。

然后我们设置了 top 值,sticky 的元素必须有 top 或者 bottom 属性,不然不会生效。

除了一些要设置的东西之外,还有一个不能设置的东西。 sticky 元素的父元素或者祖先元素不能含有 overflow:hidden 或者 overflow:auto

基本上小坑就这些,还有一个大坑叫做『只有 iOS 支持这个属性』。

Android 上实现类似效果

这里我们不得不借助 js 和 positon:fixed

添加一段 js:

        var isStopTimer = null;
        var offsetTop = $('.content-a').offset().height;

        //置顶效果
        if ($('.sticky-wrap').css("position").indexOf("-webkit-sticky") == -1) {
            $('.page-wrapper').on('touchend', function() {
                clearInterval(isStopTimer);
                isStopTimer = setInterval(function() {
                    document.body.scrollTop >= offsetTop ? $('.page-wrapper').addClass('sticky') : $('.page-wrapper').removeClass('sticky');
                }, 200)

            });

            $('.page-wrapper').on('touchmove', function() {
                document.body.scrollTop >= offsetTop ? $('.page-wrapper').addClass('sticky') : $('.page-wrapper').removeClass('sticky');
            });
        }

这里通过计算 $('.sticky-wrap').css("position").indexOf("-webkit-sticky") 是否有效来判断浏览器是否支持 sticky 属性,然后通过监听 touchmove 和 touchend 事件,在合适的时候添加一个叫 sticky 的类,这个类设置带了些样式:

.page-wrapper.sticky .sticky-wrap {
  position: fixed;
}

在需要固顶的时候我们将元素的 positon 改为 fixed,但是这里又有个坑,设置元素为 fixed 的时候,相应元素是脱离文档流的,也就是没有高度了,仔细看滑动的时候,底下的元素有一个跳动。

为了解决这个跳动,我们可以让原本在下面那个元素加点高度,然后和 sticky 元素重合,为了以后改动页面的时候不影响这个逻辑,用 js 去算高度会比较好。

加入如下代码:

        var tabContentTop = $('.ui-tab-nav').offset().height;
        var orgPaddingTop = parseInt($('.content-b').css('padding-top'));
        $('.content-b').css('padding-top', tabContentTop+orgPaddingTop);
        $('.sticky-wrap').css('margin-bottom', -tabContentTop);

这里用 padding-top 来给 content-b 增加高度,给 sticky-wrap 添加负的 margin-bottom 值来让 content-b 上来和 sticky-wrap 重合。未免覆盖原来 content-b 的 padding-top 值,最好获取一下再加,记得用 parseInt 转一下。

到这里基本坑都踩完了,虽然 Android 效果没有 iOS 那么丝滑舒服,也勉强能接受了。

最后说下调试 sticky 效果,既然是 iOS 才支持的东西,首先你要有 safari,但是平常打开是没用的,要在开发菜单那里选择 进入响应式设计模式

Tips-移动端滑动固顶效果。公众号:程序员的诗和远方

Tips-移动端滑动固顶效果。公众号:程序员的诗和远方

源码地址: https://github.com/bob-chen/demos/blob/master/sticky/html/index.html

结语

好久没发文章了,最近实在太忙,不知道是不是加班多了,还生病了,身体是革命的本钱啊,2017 祝大家身体健康。

碎碎念

记录一些所思所想,写写科技与人文,写写生活状态,写写读书感悟,主要是扯淡和感悟,欢迎关注,交流。

微信公众号:程序员的诗和远方

公众号ID : MonkeyCoder-Life

微信公众号:程序员的诗和远方