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

相关推荐
MXN_小南学前端1 分钟前
Vue + Quill:富文本的添加、传输、展示逻辑,以及 csReplyQuill 组件封装
前端·vue.js
XS0301062 分钟前
Java Web实现简易CRUD操作笔记
java·前端·笔记
Shadow(⊙o⊙)3 分钟前
qt内详解信号和槽的基本概念+实例演示
开发语言·前端·c++·qt·学习
qq_381338505 分钟前
Vue3 组合式函数设计模式:从基础封装到高级复用实战
前端·vue.js·设计模式
步十人5 分钟前
【CSS】基础一篇过
前端·css
回眸一笑吟离歌7 分钟前
edge浏览器更新后打开局域网服务报错:ERR_ADDRESS_UNREACHABLE
前端·edge
幽络源小助理11 分钟前
在线图片处理工具源码, 多功能编辑格式转换HTML单文件版
前端·html
humcomm14 分钟前
AI编程时代前端架构师的机遇和挑战
前端·架构·ai编程
adminwolf23 分钟前
自研企业微信SCRM系统源码独立部署(Golang+Vue.js)
前端·vue.js·企业微信
小短腿的代码世界24 分钟前
QwtPolar 与实时示波器级渲染优化:雷达图到示波器曲线的极限性能调优
前端·qt·架构·交互