坏了,CSS真被他们玩出花来了

前言

事情是这样子的,本人由于摸鱼过多被临时抽调去支援公司的一个官网开发,其中有个任务是改造侧边栏导航,我心想着很简单嘛,两下搞完继续我的摸鱼大业😁.然后ui就丢给了一个网站让我照着这个做(龟龟现成的都有,这也太好了),网站在这里,让我们来看一下简单的交互.

看了一下大致就是滚动到特定位置后把导航栏固定,hover的时候显示导航列表,这不是小菜一碟.

滚动到特定位置固定导航

主要逻辑就是下面这段,简单来说就是监听滚动条位置,当滚动到特定高度时改变导航按钮的定位方式, css部分是用的@emotion/styled,文档可以看一下这里.

tsx 复制代码
const [fixed, setFixed] = useState(false);
  useEffect(() => {
    const root = document.querySelector('#root');
    if (!root) return;
    const fn = () => {
      if (root.scrollTop > 451) {
        setFixed(true);
      } else {
        setFixed(false);
      }
    };
    root.addEventListener('scroll', fn);
    return () => {
      root.removeEventListener('scroll', fn);
    };
  }, []);
css 复制代码
const StyledFixed = styled.div<{ fixed?: boolean }>`
  width: 0;
  position: ${(props) => (props.fixed ? 'fixed' : 'absolute')};
  top: ${(props) => (props.fixed ? '80px' : '531px')};
  left: 80px;
  z-index: 9;
  @media (max-width: 1800px) {
    left: 12px;
  }
`;

效果如下

hover显示导航列表

然后就是鼠标移入的时候显示导航列表了,我心想这还不简单,几行css就搞定了(简单描述下就是把导航列表放在按钮里面,给按钮加上hover效果),但是当我研究了一下腾讯网站的代码,我发现事情好像没那么简单.

导航按钮和列表是平级的,这样的话鼠标移上去列表显示,但列表显示的同时hover效果也没有了,列表就又隐藏了,就会导致闪烁的效果,gif图展示不够明显.

现在问题来了,如果是平级元素,那如何控制hover显示呢,答案就在他们的父元素身上,从下面2张图上可以看出,导航按钮以及他的父元素都加上了hover样式

默认情况下父元素宽度为0,防止误触发列表展示,hover状态下设置width:auto

实现效果

到这里,我已经完全清楚了实现原理,完整的代码在下面

tsx 复制代码
const LeftNav = observer(() => {
  const { selectedKeys, openKeys } = menuStore;
  const [fixed, setFixed] = useState(false);
  useEffect(() => {
    const root = document.querySelector('#root');
    if (!root) return;
    const fn = () => {
      if (root.scrollTop > 451) {
        setFixed(true);
      } else {
        setFixed(false);
      }
    };
    root.addEventListener('scroll', fn);
    return () => {
      root.removeEventListener('scroll', fn);
    };
  }, []);
  const onMenuClick = ({ key }: { key: string }) => {
    menuStore.selectedKeys = [key];
    if (key) {
      const dom = document.querySelector(`#${key}`);
      menuStore.scrollingKey = key;
      dom?.scrollIntoView({
        behavior: 'smooth'
      });
    }
  };

  const onOpenChange = (keys: string[]) => {
    menuStore.openKeys = keys.filter((i) => i !== openKeys[0]);
  };

  return (
    <StyledFixed fixed={fixed}>
      <div className='left-nav-btn'>
        <RightOutlinedIcon />
      </div>
      <div className='left-nav-list'>
        <StyledMenuWrap>
          <Menu selectedKeys={selectedKeys} openKeys={openKeys} mode='inline' items={MENU} onClick={onMenuClick} onOpenChange={onOpenChange} />
        </StyledMenuWrap>
      </div>
    </StyledFixed>
  );
});

const StyledFixed = styled.div<{ fixed?: boolean }>`
  width: 0;
  position: ${(props) => (props.fixed ? 'fixed' : 'absolute')};
  top: ${(props) => (props.fixed ? '80px' : '531px')};
  left: 80px;
  z-index: 9;
  &:hover {
    width: auto;
    .left-nav-btn {
      display: none;
    }
    .left-nav-list {
      transform: none;
      visibility: visible;
    }
  }
  @media (max-width: 1800px) {
    left: 12px;
  }
  .left-nav-btn {
    position: absolute;
    top: 80px;
    width: 40px;
    font-size: 14px;
    font-style: normal;
    font-weight: 500;
    line-height: 20px;
    padding: 12px;
    border-radius: 100px;
    border: 1px solid #fff;
    background: rgba(255, 255, 255, 0.8);
    box-shadow: 0px 4px 30px 0px rgba(12, 25, 68, 0.05);

    cursor: pointer;

    &::before {
      content: '页面导航';
      background: linear-gradient(139deg, #c468ef 5.3%, #2670ff 90.91%);
      background-clip: text;
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }

    &:hover {
      display: none;
      & + .left-nav-list {
        transform: none;
        visibility: visible;
      }
    }
    @media (min-width: 1799px) {
      display: none;
    }
  }
  .left-nav-list {
    position: relative;
    width: 172px;
    z-index: 1;
    transition: all 0.3s ease-in;
    @media (max-width: 1800px) {
      transform: translateX(-200px);
      visibility: hidden;
    }
  }
`;

最终的实现效果如下

至于腾讯网站中的滚动到对应模块高亮菜单的实现可以看看 IntersectionObserver 这个api,好了本次的分享就到此为止了,感谢各位大佬的阅读与点赞😁,你可以说我菜,因为我是真的菜.

相关推荐
JianZhen✓1 分钟前
现在在本地开发了一些代码A,又有了新需求要紧急开发代码B需要只上线代码B的代码,如何更好的处理这种情况
前端
郝学胜-神的一滴21 分钟前
Cesium绘制线:从基础到高级技巧
前端·javascript·程序人生·线性代数·算法·矩阵·图形渲染
蒙奇D索大1 小时前
【计算机网络】408计算机网络高分指南:物理层编码与调制技术精讲
java·前端·学习·计算机网络
无盐海1 小时前
CSRF漏洞攻击(跨站请求伪造攻击)
前端·csrf
慧一居士1 小时前
CSS3 全部功能点介绍,使用场景,对应功能点完整使用示例
前端
烛阴1 小时前
深入Lua包(Package)与依赖管理
前端·lua
IT_陈寒2 小时前
5个Vue3性能优化技巧,让你的应用提速50% 🚀(附实测对比)
前端·人工智能·后端
god002 小时前
chromium项目中添加源文件(BUILD.gn项目中添加源文件)
java·服务器·前端
快乐非自愿2 小时前
Vue 缓存之坑,变量赋值方式和响应式数据
前端·vue.js·缓存
Github掘金计划2 小时前
别再用 “臃肿监控” 了!这款轻量监控神器开源 3 月狂揽 1.3k Star!
前端·监控