你所不知道的CSS负值技巧与细节
写本⽂的起因是,⼀天在⾥有同学说误打误撞下,使⽤负的outline-offset实现了加号。嗯?好奇的我马上也动⼿尝试了下,到底是如何使⽤负的 outline-offset 实现加号呢?
使⽤负值 outline-offset 实现加号
假设我们有这样⼀个简单的结构:
<div></div>
div {
width: 200px; height: 200px;
outline: 20px solid #000;
outline-offset: 10px;
}
修改outline-offset到⼀个合适的负值,那么在恰当的时候,outline 边框就会向内缩进为⼀个加号。
经过⼀番尝试,修改上述 div 的outline-offset为 -118px。
div {
width: 200px; height: 200px;
outline: 20px solid #000;
outline-offset: -118px;
}
加个动画效果,⼤概是这样:
很有意思,我尝试了很多不同的情况,最后总结了⼀个简单的规律,要使⽤负的 outline-offset ⽣成⼀个加号有⼀些简单的限制:
容器得是个正⽅形
outline 边框本⾝的宽度不能太⼩
outline-offset 负值 x 的取值范围为: -(容器宽度的⼀半 + outline宽度的⼀半) < x < -(容器宽度的⼀半 + outline宽度)
在这个例⼦后,我⼜想,CSS 属性可以取负值的地⽅有很多。⼤家最为熟知的就是负margin,使⽤负的 marign,可以⽤来实现类似多列等⾼布局、垂直居中等等。那还有没有其他⼀些有意思的负值使⽤技巧呢?
下⽂就再介绍⼀些 CSS 负值有意思的使⽤场景。
单侧投影
先说单侧投影,关于box-shadow,⼤部分时候,我们使⽤它都是⽤来⽣成⼀个两侧的投影,或者⼀个四侧的投影。如下:
OK,那如果要⽣成⼀个单侧的投影呢?
我们来看看 box-shadow 的⽤法定义:
{
box-shadow: none | [inset? && [ <offset-x> <offset-y> <blur-radius>? <spread-radius>? <color>? ] ]#
}
以box-shadow: 1px 2px 3px 4px #333为例,4 个数值的含义分别是,x ⽅向偏移值、y ⽅向偏移值、模糊半径、扩张半径。
这⾥有⼀个⼩技巧,扩张半径可以为负值。
css设置文字垂直居中继续,如果阴影的模糊半径,与负的扩张半径⼀致,那么我们将看不到任何阴影,因为⽣成的阴影将被包含在原来的元素之下,除⾮给它设定⼀个⽅向的偏移量。所以这个时候,我们给定⼀个⽅向的偏移值,即可实现单侧投影:
使⽤ scale(-1) 实现翻转
通常,我们要实现⼀个元素的 180° 翻转,我们会使⽤transform: rotate(180deg),这⾥有个⼩技巧,使⽤transform: scale(-1)可以达到同样的效果。看个Demo:
<p class="scale">CSS Nagative Scale(-1)</p>
.scale {
transform: scale(1);
animation: scale 10s infinite linear;
}
@keyframes scale{
50% {
transform: scale(-1);
}
100% {
transform: scale(-1);
}
}
看看效果:
(GIF 中第⼀⾏是使⽤了transform: rotate(180deg)的效果)
使⽤负 letter-spacing 倒序排列⽂字
与上⾯ scale(-1) 有异曲同⼯之妙的是负的letter-spacing。
letter-spacing属性明确了⽂字的间距⾏为,通常⽽⾔,除了关键字normal,我们还可以指定⼀个⼤⼩,表⽰⽂字的间距。像这样:<p class="letter_spacing">倒序排列⽂字</p>
.letter_spacing {
font-size: 36px;
letter-spacing: 0px;
animation: move 10s infinite;
}
@keyframes move {
40% {
letter-spacing: 36px;
}
80% {
letter-spacing: -72px;
}
100% {
letter-spacing: -72px;
}
}
我们设置⽂字的letter-spacing从 0 -> 36px -> -72px,观察不同的变化:
然⽽,受到中英⽂混排或者不同字体的影响,以及倒序后的排列⽅式,不建议使⽤这种⽅式来倒序排列⽂字。
transition-delay 及 animation-delay 的负值使⽤,⽴刻开始动画
我们知道,CSS 动画及过渡提供了⼀个 delay 属性,可以延迟动画的进⾏。
考虑下⾯这个动画:
简单的代码⼤概是这样:
<div class="g-container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
.item {
transform: rotate(0) translate(-80px, 0) ;
}
.item:nth-child(1) {
animation: rotate 3s infinite linear;
}
.item:nth-child(2) {
animation: rotate 3s infinite 1s linear;
}
.item:nth-child(3) {
animation: rotate 3s infinite 2s linear;
}
@keyframes rotate {
100% {
transform: rotate(360deg) translate(-80px, 0) ;
}
}
如果,我们想去掉这个延迟,希望在⼀进⼊页⾯的时候,3 个球就是同时运动的。这个时候,只需要把正向的 animation-delay 改成负向的即可。
.item:nth-child(1) {
animation: rotate 3s infinite linear;
}
.item:nth-child(2) {
animation: rotate 3s infinite -1s linear;
}
.item:nth-child(3) {
animation: rotate 3s infinite -2s linear;
}
这⾥,有个⼩技巧,被设置了animation-dealy为负值的动画会⽴刻执⾏,开始的位置是其动画阶段中的⼀个阶段。所以,动画在⼀开始的时刻就是下⾯这样:
以上述动画为例,⼀个被定义执⾏ 3s 的动画,如果animation-delay为 -1s,起点相当于正常执⾏时,第2s(3-1)时的位置。
负值 margin
负值 margin 在 CSS 中算是运⽤的⽐较多的,元素的外边距可以设置为负值。
在 flexbox 布局规范还没流⾏之前,实现多⾏等⾼布局还是需要下⼀番功夫的。其中⼀种⽅法便是使⽤正 padding 负 margin 相消的⽅法。
有如下⼀个布局:
左右两栏的内容都是不确定的,也就是⾼度未知。但是希望⽆论左侧内容较多还是右侧内容较多,两栏的⾼度始终保持⼀致。
OK,其中⼀种 Hack 办法便是使⽤⼀个很⼤的正 padding 和相同的负 margin 相消的⽅法填充左右两栏:
.g-left {
...
padding-bottom: 9999px;
margin-bottom: -9999px;
}
.g-right {
...
padding-bottom: 9999px;
margin-bottom: -9999px;
}
可以做到⽆论左右两栏⾼度如何变化,⾼度较低的那⼀栏都会随着另外⼀栏变化。
具体的代码可以看看这⾥:
总结⼀下
另外,还有⼀些⼤家熟知的没有单独列出来的,譬如:
使⽤负 marign 实现元素的⽔平垂直居中
使⽤负 marign隐藏列表 li ⾸尾多余的边框
使⽤负 text-indent 实现⽂字的隐藏
使⽤负的 z-index 参与层叠上下⽂排序
还有⼀些很深奥的,譬如张鑫旭⼤⼤在今年的 CSS ⼤会上分享的,利⽤负的 opacity 在 CSS 中实现了伪条件判断,配合 CSS ⾃定义属性,使⽤纯 CSS 实现360° 的饼图效果:
最后
额,虽然 CSS 负值的⼀些使⽤场景的确有有⽤之处,但是与此同时有可能带来的是代码可读性的下降。有的时候看到这些代码不得不好好捋⼀捋才能缓过神来,再感叹⼀句,原来如此。
如果有其他更好的更易理解的实现⽅式,具体使⽤实现的时候应该好好权衡⼀下。
好了,本⽂到此结束,希望对你有帮助 :)
更多精彩 CSS 技术⽂章汇总在我的,持续更新,欢迎点个 star 订阅收藏。
如果还有什么疑问或者建议,可以多多交流,原创⽂章,⽂笔有限,才疏学浅,⽂中若有不正之处,万望告知。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。