最近有一个非常振奋人心的消息,CSS即将原生支持嵌套——Agenda+发布Nesting的FPWD[1],表明CSS嵌套规范即将进入规范的FWPD阶段。目前对应的规范是——CSSNestingModule[2]。随着CSS自定义属性(CSSVariable)的大规模兼容,CSS现在将支持嵌套,预处理器的一些核心功能已经被CSS原生支持。这是否意味着像SASS/LESS这样的预处理器就没用了??即将被淘汰?规范的几个阶段首先简单介绍一下一个规范从提案到实施会经历的几个阶段:)CandidateRecommendation(CR)Transition-ProposedRecommendationTransition–ProposedRecommendations(PR)Recommendation(REC)上面说了即将进入FPWD,但是只是在第二个specificationPhaseWD阶段,FPWD的意思是第一个publicworking草案(第一份公共工作草案(FPWD))。FPWD之后将有几个工作草案,处理来自CSSWG内外更广泛社群的反馈。完善的标准化设计。也就是说,目前来看,即便后续过程顺利,但距离浏览器大规模落地规范的那一天,还有很长一段时间。另外,我觉得像SASS\LESS这样的预处理器有一些有趣的功能(functions),即使在原生CSS支持自定义属性和嵌套之后,这些功能还是有所欠缺。我简单列举一下我的看法。for()循环函数目前,原生CSS还不支持循环函数。但实际上,在预处理器中,循环仍然是一个比较常用的函数。考虑如下布局:ul下有多个li,每个li的高度增加20px。当然也可以一个一个写,但是有个循环其实可以大大减少工作量:
如果没有预处理器,我们的CSS可能看起来像这样:ul{display:flex;align-items:baseline;justify-content:space-between;}li{width:50px;background:#000;}li:nth-child(1){height:20px;}li:nth-child(2){height:40px;}//...3~9li:nth-child(10){height:200px;}如果使用SASS预处理器,可以简化为:ul{display:flex;align-items:baseline;justify-content:space-between;}li{width:50px;background:#000;}@for$ifrom1through10{li:nth-child(#{$i}){height:calc(#{$i}*20px);}}当然,除此之外,在很多复杂的CSS动画效果中,Looping是一个非常非常常见的特性。比如一些粒子动画,我们通常需要操作50-100个粒子,也就是50-100个div样式,甚至更多。如果没有循环,一条条写的效率会大大降低。演示使用预处理器循环函数实现的一些效果。下面我简单罗列一些我自己实现并应用于CSS预处理器循环功能的动画效果。像上面使用纯CSS实现的火焰效果,火焰的动态燃烧效果。其实是通过大量微粒子的运动和过滤器的配合来实现的。使用了SASS的loop函数的片段:@for$ifrom1to200{.g-ball:nth-child(#{$i}){$width:#{random(50)}px;width:$width;height:$width;left:calc(#{(random(70))}px-55px);}.g-ball:nth-child(#{$i}){animation:movetop1slinear-#{random(3000)/1000}sinfinite;}}咦,上面的循环循环了200次之多。如果真的要一一写,工作量还是非常巨大的。上述效果的完整代码可以点击这里:CodePenDemo--CSSCandles[3]if()条件语句下面是if()条件语句。其实CSS中有一种写法和条件语句很相似,就是mediaquery@media和featuredetection@supports语句。目前CSS支持的类似条件选择的一些写法如下:@support条件语句CSS@supports使用CSS语法实现特征检测,在CSS块内部写上如果特征检测通过你要实现的CSS语句.div{position:fixed;}@supports(position:sticky){div{position:sticky;}}上面的CSS语句的意思是,如果客户端支持position:sticky,则使用position:sticky,否则使用position:fixed。关于CSS特征检测的深入讲解,可以看看我的文章:深入探讨CSS特征检测@supports和Modernizr[4]@mediaconditionalstatement另外一个常见的条件语句是mediaquery,目前还是大家耳熟能详的。如果当前设备满足某个条件,那么如何满足。article{padding:4rem;}@mediascreenand(min-width:900px){article{padding:1rem3rem;}}嗯,上面两个条件语句可以互相嵌套:@supports(display:flex){@mediascreenand(min-width:900px){article{display:flex;}}}然而,以上两个毕竟不是我们严格意义上期望的if()语句。很久以前,社区就有声音(css-values-if()function[5]),提议在CSS规范中实现if()条件语句,类似这样:.foo{--calc:calc(10*(1vw+1vh)/2);font-size:if(var(--calc)<12px,12px,var(--calc));}可以看到这条语句if(var(--calc)<12px,12px,var(--calc))类似于三元语句,比较容易理解。但是上面的条件语句一直没有被支持的原因可以看scss-values-if()函数[6]。原因是CSS一直试图避免属性之间的任意依赖。在CSS中,属性之间存在一些隐含的依赖关系,比如em单位长度受父元素font-size的影响,如果作者可以任意添加依赖关系(通过if()条件语句),会造成一些问题。译文是:不幸的是,这意味着我们在属性之间添加任意依赖关系,到目前为止我们一直避免这样做,因为它通常无法解析。自定义属性可以任意相互引用,但它们的作用有限,并且当我们注意到一个循环时,它们具有某种合理的“只是变得无效”的行为。对于任意CSS,循环更难确定,而且更容易发生,因为存在许多现有的、隐式的属性间依赖关系。例如,任何需要长度的东西都依赖于font-size(由于em),所以你不能在font-size中有一个值来引用一个需要长度的属性(所以没有调整font-size来缩放有宽度!)。随着时间的推移,我们会添加此类新的依赖项(例如添加lh单元,这会导致对行高的依赖项);如果作者可以添加任意依赖项,我们将无法添加新的隐式依赖项s害怕破坏现有内容(通过形成以前有效且非循环的循环)。所以CSS中直接的if()语句并没有实现。SASS等预处理器中的if()语句最后,我们来看看预处理器中if()的使用。由于SASS等预处理器还是要编译CSS文件,所以if()其实并不是很常用。因为SASS中的if()无法实现类似于上面提到的font-size:if(var(--calc)<12px,12px,var(--calc))的功能。在SASS中,我觉得最常用的if()可能是这种场景:@mixintriangle($size,$color,$direction){height:0;width:0;border-color:transparent;border-style:solid;边框宽度:$size;@if$direction==up{border-bottom-color:$color;}@elseif$direction==right{border-left-color:$color;}@elseif$direction==down{border-top-color:$color;}@elseif$direction==left{border-right-color:$color;}@else{@error"Unknowndirection#{$direction}.";}}.next{@includetriangle(5px,black,right);}以上代码是对三角形CSS实现的封装,通过传入的参数,实现不同方向、颜色、大小的三角形。也就是说,预处理器中的if()更多地完成了一些功能的封装,方便复用。其实上面的代码会被编译成:.next{height:0;width:0;border-color:transparent;border-style:solid;border-width:5px;border-left-color:black;}Random()随机函数OK,接下来就是随机函数,这是SASS等预处理器中最常用的函数。目前本机CSS不支持任何形式的随机化。在CSS动画效果中,我们不希望很多因素是静态的。我们希望的是我们设定一个基本的规则,让他们在一个范围内,这样每次刷新都能产生不同的值。影响。最常见的是不同的颜色、不同的长度、不同的数量等等。例如,使用CSS实现的如下效果:夏日日落图[7]。通过random,我们可以在每次刷新时得到不同高/宽、不同位置的div块,利用随机特性绘制出不同的效果图:DEMO——夏日夕阳图[8]目前原生CSS不支持任何形式的随机化。使用预处理器只能在编译前编写随机函数。SASS中一些更常用的随机函数是:$r:random(100);random()是SASS支持的函数。上面的$r是0到100之间的随机整数,可以得到。使用random()可以封装各种随机函数,比如随机颜色:@functionrandomNum($max,$min:0,$u:1){@return($min+random($max))*$u;}@functionrandomColor(){@returnrgb(randomNum(255),randomNum(255),randomNum(255));}div{background:randomColor();}在原生CSS中实现random()的一些思考下面是一个社区关于在原生CSS中实现random()函数的一些思考,有兴趣的可以扫一扫:[css-values]random()函数[9]简单传达了一些比较有意思的观点。假设CSS原生实现了random()函数,例如:
123456789789.foo:hover{color:rgb(random(0,255),0,0);}假设ramdom()是原生CSS实现的随机函数,有一些需要大家解决或者认可的事情:random(0,255)的值是什么时候确定的,是每次解析CSS的时候,还是每次应用触发的时候?上面的DEMO,三个.foo的颜色值是一样的吗?对于重复悬停,取消悬停状态,random(0,255)的值会不会改变?上述问题可以归结为如果CSS原生支持随机性,则必须解决随机值的持久化和更新问题。总之,看来以后CSS原生支持随机性的可能性还是很大的。工具函数:颜色函数、数学函数最后,我们来看看一些有趣的工具函数。目前,原生CSS不支持一些复杂的颜色函数和数学函数。但是预处理器带有这些功能。在我之前关于阴影的文章——你不知道的CSS阴影技巧与细节[10]中,介绍了一个利用多重阴影实现三维阴影的效果。比如我们要实现如下效果:其中阴影的颜色变化使用SASS的颜色函数:fade-out改变颜色的透明度使颜色更透明desaturate改变颜色的饱和度值到降低颜色饱和度@functionmakelongrightshadow($color){$val:0px0px$color;@for$ifrom1through50{$color:fade-out(desaturate($color,1%),.02);$val:#{$val},#{$i}px#{$i}px#{$color};}@return$val;}p{text-shadow:makelongrightshadow(hsla(14,100%,30%,1));}的当然,除了上面两个颜色函数,SASS还提供了很多类似颜色相关的函数,可以看这里:SassBasics-ColorFunctions[11]。除了颜色,CSS效果中还经常使用数学函数。在这篇文章——CSS中使用三角函数绘制曲线图形和显示动画[12]中,我专门讲了如何使用SASS等预处理器实现三角函数实现曲线,实现一些有趣的效果,比如它是像这样:当然目前SASS不支持三角函数,但是我们可以用SASS函数实现一组三角函数代码:@functionfact($number){$value:1;@if$number>0{@对于$ifrom1through$number{$value:$value*$i;}}@return$value;}@functionpow($number,$exp){$value:1;@if$exp>0{@for$ifrom1through$exp{$value:$value*$number;}}@elseif$exp<0{@for$ifrom1through-$exp{$value:$value/$number;}}@return$value;}@functionrad($angle){$unit:unit($angle);$unitless:$angle/($angle*0+1);@if$unit==deg{$unitless:$unitless/180*pi();}@return$unitless;}@functionpi(){@return3.14159265359;}@functionsin($angle){$sin:0;$angle:rad($angle);//Iterateabunchoftimes.@for$ifrom0through20{$sin:$sin+pow(-1,$i)*pow($angle,(2*$i+1))/fact(2*$i+1);}@return$sin;}@functioncos($angle){$余弦:0;$angle:rad($angle);//Iterateabunchoftimes.@for$ifrom0through20{$cos:$cos+pow(-1,$i)*pow($angle,2*$i)/fact(2*$i));}@return$cos;}@functiontan($angle){@returnsin($angle)/cos($angle);}就目前的原生CSS而言,在数学函数方面做了很多努力,例如:基本计算函数calc()比较函数max()、min()、clamp()和其他兼容性已经逐步推出,可以大规模使用,类似指数函数pow(),sqrt(),hypot(),log(),exp()三角函数sin(),con(),tan()步骤函数round()、mod()、rem()等也在规范CSSValuesandUnitsModuleLevel4[13]中被提及和定义。相信在不久的将来,社区中一些关于数学函数的讨论会逐渐落地。有兴趣的也可以看这里:MathematicalExpressions[14]总结一下。综上所述,就目前而言,我认为SASS/LESS等预处理器在很多方面还是有用的。在上述一些功能性的原生CSS没有完全实现之前,预处理器可以在一定程度上弥补CSS的不足。而且,除了上面提到的一些我个人认为比较重要和有趣的功能和功能外,预处理器的其他核心功能,如extend、mixins等,也能有效提高开发效率。所以,在未来的一段时间里,我觉得预处理器还是可以和CSS友好共存的~最后,本文到此结束,希望对你有所帮助:)参考[1]Agenda+发布FPWD嵌套:https://lists.w3.org/Archives/Public/www-style/2021Mar/0019.html[2]CSS嵌套模块:https://drafts.c??sswg.org/css-nesting/[3]CodePenDemo--CSSCandles:https://codepen.io/Chokcoco/pen/jJJbmz[4]深入探讨CSS特征检测@supports和Modernizr:https://www.cnblogs.com/coco1s/p/6478389.html【5】css-values-if()函数:https://github.com/w3c/csswg-drafts/issues/3455[6]scss-values-if()函数:https://github.com/w3c/csswg-drafts/issues/3455[7]夏日日落图:https://csscoco.com/inspiration/#/./cssdoodle/sunset[8]DEMO——夏日日落图:https://csscoco.com/inspiration/#/./cssdoodle/sunset[9][css-values]random()函数:https://github.com/w3c/csswg-drafts/issues/2826[10]CSS你不知道的影子技巧和细节:https://github.com/chokcoco/iCSS/issues/39[11]Sass基础——颜色函数:https://www.sass.hk/skill/sass25.html[12]在CSS中使用三角函数绘制曲线图形和显示动画:https://github.com/chokcoco/iCSS/issues/72[13]CSSValuesandUnits模块级别4:https://drafts.c??sswg.org/css-values-4/#exponent-funcs[14]数学表达式:https://drafts.c??sswg.org/css-values/#math