注册
web

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

前言


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


iShot_2023-10-30_11.07.56.gif


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


滚动到特定位置固定导航


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


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 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;
}
`;

效果如下


iShot_2023-10-30_11.24.58.gif


hover显示导航列表


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


image.png


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


iShot_2023-10-30_11.49.33.gif

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


image.png


image.png


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


实现效果


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


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;
}
}
`
;

最终的实现效果如下
iShot_2023-10-30_14.39.20.gif


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


作者:hahayq
来源:juejin.cn/post/7295343805020487690

0 个评论

要回复文章请先登录注册