坏了,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,好了本次的分享就到此为止了,感谢各位大佬的阅读与点赞😁,你可以说我菜,因为我是真的菜.

相关推荐
mldong32 分钟前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排1 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60612 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角3 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!3 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰3 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi5 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen5 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马6 小时前
AI源码分析:拆解模型底层实现逻辑
前端
BillKu6 小时前
全局样式变量:CSS 自定义属性(--border-color)和SCSS 变量($border-color)的说明
javascript·css·scss