移动端吸顶方案

本文介绍三种方式做移动端吸顶,记录不同方式下遇到的问题及思考.

1-sticky做吸顶

sticky定位没有导致元素脱离文档流

sticky失效

sticky兼容性查询:好用的查询网址

什么时候,会导致粘贴定位失效呢?(下面这两种是网上已有的)
1.父级元素设置了overflow:hidden
2.包裹的最近的父级元素高度小于等于sticky元素的高度会失效

自己遇到的一个sticky失效原因竟然是双层tab吸顶问题

在这里插入图片描述

在这里插入图片描述

原因:

第一层tab是手写的样式,第二层tab使用的是ant.design中的tab组件,显然出现问题原因是,第二层tab导致的内容过长出现了滚动条,目前这个sticky定位的父元素是高于sticky元素的,但是定位还是失效了。
在这里插入图片描述
在这里插入图片描述

sticky文字抖动问题

这是需要在IOS系统上,使用小于100%的比例或者大于100%比例才会出现的问题
详细见链接视频:
B站视频

sticky定位,当使用rem单位时,会出现细线问题

B站视频

这个在IOS手机上很明显,在安卓机上,有时能看的出来,细线出现的原因是后面有背景色,rem设置高度的元素不能紧凑的连接起来,而且由于抖动问题,所以细线就此产生

使用变量控制类名,从而转换样式

如果ts报错,不能把null赋值给element,
使用下面这条语句,就能没有ts类型错误

使用IntersectionObserver对象监听是否吸顶

useEffect(() => {
    fetch('http://localhost:80/admin/story', {
      method: 'get',
    }).then((response) => response.json())
    .then((res) => {
      // console.log(res.data.rows)
      handlerStory(res.data.rows)
    }).catch((err) => {
      console.log(err, 'error');
    })
    const dom: HTMLElement = watchDOM.current  as unknown as HTMLElement;
    let isVisibility = true;
    const io = new IntersectionObserver(() => {
      isVisibility = !isVisibility;
      setIsTop(isVisibility)
    })
    io.observe(dom);
    return () => {
      io.disconnect();
    }
  }, [])

为什么使用类名比使用dom修改样式更好?

在这里插入图片描述

chrome 中渲染流水线的流程

在这里插入图片描述

一个 html 是怎么被处理成一个页面的。html 中 dom 部分生成 dom tree,css 部分生成 stylesheet,dom tree 在解析完后会等待 stylesheet 构建完再渲染。stylesheet 根据默认样式、样式继承、css 选择器规则、样式优先级等规则,找到对应的 dom 节点赋予它样式,形成 dom 结构+样式的 render tree。其中有些节点是不可见的(不是 opacity: 0,而是诸如 display: none 这样的),它们不会影响其他节点的位置,在渲染时不需要考虑,所以过滤掉这些节点之后生成 layout tree,layout tree 会根据节点之间的相互影响生成它们的位置信息(reflow 回流/重排)。

dom tree —>stylesheet —> render tree —> 节点是否可见—> layout tree(节点的位置信息)

你以为到了 layout tree 这一步终于可以渲染了吗,还远没有。layout tree 会根据某些 css 属性分层,比如 position: absolute; position: fixed 等等。如果它们发生更新,不需要连带其他节点 reflow,所以分层有利于单独处理。然后每个图层会生成各自的绘制指令列表(repaint 重绘),很底层的命令,描述了每一个点每一条线如何绘制。

layout tree —> 分层 ----> repaint–>合成层(优先处理可视区图块)–>biz组件–>显示器的后缓冲区–>前后缓冲区交换,显示下一帧

gpu绘制:图块的绘制命令会通过光栅化线程池交给 gpu 绘制

你以为生成了绘制命令终于可以渲染了吗,还远没有。它们会被交给合成层,顾名思义,它是负责将那些图层合并的。它并不会全量的处理整个页面,而是优先处理可见视口附近的图块,如果页面过于复杂,它还会先给出低分辨率的位图。图块的绘制命令会通过光栅化线程池交给 gpu 绘制。合成层拿到 gpu 绘制出来的位图后,将它们合成为一张位图,这就是当前页面。你觉得终于渲染完了吗,并没有。它会将位图交给浏览器进程里的 biz 组件,biz 组件会交给显示器的后缓冲区,当显示器需要显示下一帧之前,前后缓冲区交换,屏幕上终于展示出新渲染的页面帧。(这里补个小知识,requestAnimationFrame 的背后原理就是显示器发送了 sync 信号,渲染进程将 requestAnimationFrame 回调放进消息队列,从而实现了 js 未阻塞的情况下 requestAnimationFrame 可以随帧调用)

可以发现浏览器想渲染一帧页面要经过如此多的步骤,是不是真为它的性能捏一把汗,这也是 HTML 方便开发带来的代价。我们还可以看出整个流程中最昂贵的步骤就是 reflow 和 repaint 了,至于合成层那边主要是和 GPU 打交道,不会占用渲染线程(也就是执行 js 的线程),并且 GPU 本来就十分擅长处理图片,所以合成层的工作很快。

因此,性能优化的核心思路其实就是尽可能减少 reflow、repaint 的工作,尽可能多利用合成层的工作。比如 css 硬件加速,包括 transform3D、opacity、willchange 等。拿 transform3D 来说,其实它只是图层的位移、转换,并不影响其他图层,所以不会经过 reflow 和 repaint,直接在合成层处理,GPU 处理这种变换非常快。因此硬件加速技巧可以极大的优化 css 性能。

下面谈一下滚动操作带来的影响在渲染流水线中的处于什么位置。首先滚动可能会产生滚动条,它的突然出现影响了其他元素的布局位置,会触发 reflow 以及后面的所有流程。滚动过程中,前面说过合成层初始优先处理可见视口附近的图块,其他部分其实还没有处理,元素结构太复杂时滚动过快可能让合成层来不及处理,从而出现白屏区域。还有比如 position: fixed 的元素,会跟着滚动走,那么它也会在滚动中 repaint 的。

性能对比:

使用变量+IntersectionObserver+sticky
在这里插入图片描述

使用变量+scroll +sticky

在这里插入图片描述

使用变量+scroll +sticky+debounce(防抖500ms)
在这里插入图片描述

使用dom +scroll +sticky+debounce(防抖500ms)
在这里插入图片描述

使用dom +scroll +sticky
在这里插入图片描述

2-使用固定定位做吸顶-fixed

固定定位由于脱离文档流,所以计算监听元素与吸顶元素之间的距离比较难以计算,计算出现误差就会导致页面出现弹跳现象
fiexd脱离正常文档流

可以在吸顶的这个时候做样式修改
不需要在使用父盒子设置高度给吸顶元素做保底
还是会产生rem细线问题
但是文字不会产生抖动效果

脱离文档流后,宽度100%包含了滚动条的宽度

在移动端由于滚动条很小,所以影响很小,但是还是能够看出来,对不齐了
在这里插入图片描述

获取页面内滚动条宽度

function getScrollbarWidth() {
  if (scrollbarWidth !== null) {
    return scrollbarWidth;
  }

  if (typeof document !== 'undefined') {
    const div = document.createElement('div');
    const newStyles = {
      width: '100px',
      height: '100px',
      position: 'absolute',
      top: '-9999px',
      overflow: 'scroll',
      MsOverflowStyle: 'scrollbar',
    };

    Object.keys(newStyles).map((style) => {
      // @ts-ignore
      div.style[style] = newStyles[style];
    });

    document.body.appendChild(div);
    scrollbarWidth = div.offsetWidth - div.clientWidth;
    document.body.removeChild(div);
  } else {
    scrollbarWidth = 0;
  }

  return scrollbarWidth || 0;
}


B站视频

fixed吸顶元素与监听元素的距离,监听元素高度过高,会导致内容跳动,高度过低,会导致吸顶元素与内容脱轨

3-使用内容fiexd,达到吸顶效果

当页面出现两个滚动条时,滚动触发时机无法掌控

设置overflow:scroll属性的元素,一定要设置高度,不然无法产生滚动条

可能第一个滚动条滚动到底,才开始滚动第二个滚动条,也可能第一个滚动条滚动一半,开始滚动第二个滚动条,还可能先滚动第二个滚动条,第一个滚动条不滚动,取决于鼠标所在位置.

鼠标在滚动第一个滚动条时,由于页面滚动,鼠标处于第二个滚动元素上面,如果此刻鼠标移动了,第二个滚动条开始滚动, 第二个滚动条滚动完,如果第一个滚动条还有未滚动的位置,第一个滚动条继续滚动;–1

如果鼠标不移动,第一个滚动条滚动,滚动完成之后,第二个滚动条不会滚动;----2

鼠标如果在第一个产生滚动的元素上面,只滚动第一个滚动条;----3

如果鼠标位于第二个产生滚动元素身上,则只有第二个元素滚动,第一个滚动条不滚动;----4

B站视频

所以争对上面的效果,内容fixed无法达到效果,
不过如果是tab固定定位,这样实现效果也很好
在这里插入图片描述

参考资料:
https://mp.weixin.qq.com/s/hFG1ypsEckVIZb5OCv0H7g
https://www.hongkiat.com/blog/writing-better-css/

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值