Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

不可思议的CSS导航栏下划线跟随效果 #33

Open
chokcoco opened this issue Mar 26, 2018 · 17 comments
Open

不可思议的CSS导航栏下划线跟随效果 #33

chokcoco opened this issue Mar 26, 2018 · 17 comments

Comments

@chokcoco
Copy link
Owner

chokcoco commented Mar 26, 2018

先上张图,如何使用纯 CSS 制作如下效果?

underline

在继续阅读下文之前,你可以先缓一缓。尝试思考一下上面的效果或者动手尝试一下,不借助 JS ,能否巧妙的实现上述效果。

OK,继续。这个效果是我在业务开发的过程中遇到的一个类似的小问题。其实即便让我借助 Javascript ,我的第一反应也是,感觉很麻烦啊。所以我一直在想,有没有可能只使用 CSS 完成这个效果呢?

定义需求

我们定义一下简单的规则,要求如下:

  • 假设 HTML 结构如下:
<ul>
  <li>不可思议的CSS</li>
  <li>导航栏</li>
  <li>光标小下划线跟随</li>
  <li>PURE CSS</li>
  <li>Nav Underline</li>
</ul>
  • 导航栏目的 li 的宽度是不固定的
  • 当从导航的左侧 li 移向右侧 li,下划线从左往右移动。同理,当从导航的右侧 li 移向左侧 li,下划线从右往左移动。

实现需求

第一眼看到这个效果,感觉这个跟随动画,仅靠 CSS 是不可能完成的。

如果想只用 CSS 实现,只能另辟蹊径,使用一些讨巧的方法。

好,下面就借助一些奇技淫巧,使用 CSS 一步一步完成这个效果。分析一下难点:

宽度不固定

第一个难点, li 的宽度是不固定的。所以,我们可能需要从 li 本身的宽度上做文章。

既然每个 li 的宽度不一定,那么它对应的下划线的长度,肯定是是要和他本身相适应的。自然而然,我们就会想到使用它的 border-bottom

li {
    border-bottom: 2px solid #000;
}

那么,可能现在是这样子的(li 之间是相连在一起的,li 间的间隙使用 padding 产生):

image

默认隐藏,动画效果

当然,这里一开始都是没有下划线的,所以我们可能需要把他们给隐藏起来。

li {
    border-bottom: 0px solid #000;
}

推翻重来,借助伪元素

这样好像不行,因为隐藏之后,hover li 的时候,需要下划线动画,而 li 本身肯定是不能移动的。所以,我们考虑借助伪元素。将下划线作用到每个 li 的伪元素之上。

li::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border-bottom: 2px solid #000;
}

下面考虑第一步的动画,hover 的时候,下划线要从一侧运动展开。所以,我们利用绝对定位,将 li 的伪元素的宽度设置为0,在 hover 的时候,宽度从 width: 0 -> width: 100%,CSS 如下:

li::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 0;
    height: 100%;
    border-bottom: 2px solid #000;
}

li:hover::before {
    width: 100%;
}

得到,如下效果:

navunderline

左移左出,右移右出

OK,感觉离成功近了一步。现在还剩下一个最难的问题:

如何让线条跟随光标的移动动作,实现当从导航的左侧 li 移向右侧 li,下划线从左往右移动。同理,当从导航的右侧 li 移向左侧 li,下划线从右往左移动。

我们仔细看看,现在的效果:

twounderline

当从第一个 li 切换到第二个 li 的时候,第一个 li 下划线收回的方向不正确。所以,可以能我们需要将下划线的初始位置位移一下,设置为 left: 100%,这样每次下划线收回的时候,第一个 li 就正确了:

li::before {
    content: "";
    position: absolute;
    top: 0;
    left: 100%;
    width: 0;
    height: 100%;
    border-bottom: 2px solid #000;
}

li:hover::before {
    left: 0;
    width: 100%;
}

看看效果:
twounderline11

额,仔细对比两张图,第二种效果其实是捡了芝麻丢了西瓜。第一个 li 的方向是正确了,但是第二个 li 下划线的移动方向又错误了。fxxk

神奇的 ~ 选择符

所以,我们迫切需要一种方法,能够不改变当前 hover 的 li 的下划线移动方式却能改变它下一个 li 的下划线的移动方式(好绕口)。

没错了,这里我们可以借助 ~ 选择符,完成这个艰难的使命,也是这个例子中,最最重要的一环。

对于当前 hover 的 li ,其对应伪元素的下划线的定位是 left: 100%,而对于 li:hover ~ li::before,它们的定位是 left: 0。CSS 代码大致如下:

li::before {
    content: "";
    position: absolute;
    top: 0;
    left: 100%;
    width: 0;
    height: 100%;
    border-bottom: 2px solid #000;
    transition: 0.2s all linear;
}

li:hover::before {
    width: 100%;
    left: 0;
}

li:hover ~ li::before {
    left: 0;
}

至此,我们想要的效果就实现拉!撒花。看看:

underlineawhere

效果不错,就是有点僵硬,我们可以适当改变缓动函数以及加上一个动画延迟,就可以实现上述开头里的那个效果了。当然,这些都是锦上添花的点缀。

完整的DEMO可以戳这里: CodePen --Demo

最后

本方法最大的瑕疵在于一开始进入第一个 li 的时候,线条只能是从右往左,除此之外,都能很好的实现跟随效果。

许久没更新了,最近沉迷学习区块链相关技术,譬如以太坊编程,智能合约的编写巴拉巴拉的。后面还是会把更多精力放在本行,多出一些前端文章,CSS 的魅力还是无法抵挡的。

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

好了,本文到此结束,希望对你有帮助 :)

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

@madneal
Copy link

madneal commented Mar 27, 2018

div:hover {
 border-bottom: 1px solid #000;
}

这样?

@oiahoon
Copy link

oiahoon commented Mar 27, 2018

@neal1991 你这个没有动画效果

@chokcoco
Copy link
Owner Author

@neal1991
需要下划线跟随哈。

@madneal
Copy link

madneal commented Mar 27, 2018

css 为啥这么难 😢

@oiahoon
Copy link

oiahoon commented Mar 27, 2018

居然给我找到另外一个类似的例子,你也可以一并看看。
https://codepen.io/hollow3d/pen/ENgvvX

@zgw010
Copy link

zgw010 commented Apr 4, 2018

在这里为什么要使用 before 呢,下划线跟在 li 的后面,为什么不用 after 呢?
还有为什么这里用 before 和用 after 的效果竟然是一模一样的呢?

@tujingyu
Copy link

不考虑兼容性的话,完美,主要还是低版本的IE

@SmartWuJun
Copy link

从第一个快速移动到第三个 会直接 第一个显示 接着第三个显示 跳过 第二个下划线

@mask2012
Copy link

从效果上看,因为原理不一样,比顺畅跟随的那种效果上还有差距,不过能用css搞定这个很牛逼了!

@chess99
Copy link

chess99 commented Sep 29, 2018

还想右到左的又怎么实现, 结果是默认效果
鼠标不按顺序移动的时候这效果感觉有点奇怪

@xuyakai11
Copy link

你为何如此优秀~~

@LOUSANPANG
Copy link

li 标签相隔远一点的话, 下划线出现的动画可能会有点问题😃

@rkrv01
Copy link

rkrv01 commented May 31, 2019

右浮动,再用right定位就可以实现从左到右啦

@wanghaooo
Copy link

@handlefuping
Copy link

transition: 0.2s all linear; 这段代码也是相当关键的 其实打了 li:hover ~ li::before {
left: 0;
} 一个措手不及

@9yues
Copy link

9yues commented Mar 3, 2021

css:其实我也没想到还有这种操作

@claviering
Copy link

如果是背景的话,怎么实现,我尝试了一下,但是从左到右效果不行

https://codepen.io/claviering/pen/YzQRvvg

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests