http://www.w3cplus.com/preprocessor/sass-px-to-em-with-mixin-and-function.html
CSS单位是一个很意思的东西,到目前为止,CSS的单位不仅仅局限于em,px,pt,com,in...,还出现了新的单位,比如rem
,vw
,vh
,vmin
和vmax
等等。在CSS-trick有对这些单位进行描述(可以点击这里阅读中文)。在这么多的单位中,其中px
与em
两者的互转是最令同学们头痛的。简值是一言难尽,理不清呀!
曾经在《CSS中强大的EM》一文中详细的介绍了px
转成em
的公式与使用细节,但只是仅仅看文字描述还是让人晕晕的,最好是动手写写。如果你想了解更多有关于px
转em
相关知识,建议您仔细阅读一下下面的文章:
- PXtoEM
- Taking Ems Even Further
- Taking the “Erm..” Out of Ems
- px – em – % – pt – keyword
- Understanding em Units in CSS
如果你理清楚了px
和em
之间的关系,那么你就可以使用Em Calculator工具,在线将px
转成em
:
看到上面的在线生成工具,我们应该回到今天我们要讨论的主题,如何使用Sass实现px
转成em
。简单点说,使用Sass来实现"Em Calculator"工具功能。
CSS中的px
转em
在CSS中px
转em
,大家都有相当深的体会,特别是对于一些涉及这方面不深的同学,往往都被这两者折磨死了。如果您掌握了其中的绝巧,你就不会觉得复杂了。在《CSS中强大的EM》虽然在文中详细介绍了两者的转换的详细细节,文章相对蛮长的,或许有很多同学不喜欢阅读,有的网友对此做过一些总结。
- 浏览器的默认字体大小是16px;
- 不管元素有没有设置自身字体大小,元素自身上的所有属性值(box module相关的属性)都可以按照
(1 ÷ 元素自身的font-size) × 需要转换的像素值 = em值
。元素在没有设置自身font-size
大小时,会直接继承其祖先元素的值; - 元素字体大小
font-size
的em按照如下公式计算(也就是font-size
中的em值是相对于父元素的font-size
值):(1 ÷ 父元素的font-size) × 需要转换的像素值 = em值
。
综合以上所述,我们可以简单的理解为:“默认1em=16px,而font-size
使用em
为单位时,是相对于其父元素(或祖先元素)的font-size
;而元素其他属性(有关于box module,例如padding
,border
,width
,height
,margin
等)使用em
为单位时,是相对于元素本身的font-size
值来计算。”
知道如何使用公式将px
转换成em
,但在实际使用中,给我们的工作带来很多不便之处。特别是改变基本字体的时候,那你的噩梦就即将来了。或许你会说,我可以借助Em Calculator工具来完成。但这始终不是解决问题的最佳方案。
Sass中的px
转em
我想使用Sass同学都会定义很多mixins
运用于不同的项目中。我现在就在做这样的一件事情,将很多常用的公共样式模块抽取出来,分别定义成mixins
,并且适用于每一个项目之中。今天将要做的也是类似于这样的一件事情。
有关于Sass中的px
转em
的代码片段到处可见,此处我们就不在纠结谁的代码优秀,谁的代码拙。我想我们应该了解他是怎么定义的,并且如何使用?
Sass的px
转em
要点
使用Sass来实现px
转em
的计算,有几点需要注意:
px
与em
之间的计算原理(简单点说就是数学运算);- 需要定义一个函数,并且给其命名。
px
转em
的计算
使用Sass来完成px
转成em
,其中第一步,也是非常关键的一步,需要明白两者之间怎么进行换算。前面我们也回忆了CSS中的px
向em
转换的计算。那么其原理同样可以运用到Sass中来。我们来看一个简单的例子。
假设置默认的字号font-size
定义为16px
,当你的标题一h1
使用的字号为32px
,此时,我们可以使用:
h1 {font-size: (32 / 16) * 1em} //也就是 h1 {font-size: 2em;}
此时你可能会想,我们可以使用一个mixin
来完成:
@mixin pxToem ($target-size,$context:$base-font-size){ font-size: $target-size / $context; }
@mixin pxToem
实现了具体数字计算到变量计算的转换,也实现了最初示例所示的功能。但实际使用之中,px
转em
并不仅仅用在font-size
属性,还有其他能使用长度单位的属性上。那上面的mixin
离我们的目标还很遥远,我们应该继续的思考。
修改px
转em
的mixin
在GitHub上找到了一个功能强大的mixin
,这个mixin
是Dan Adams两年前写的一个关于px
转em
的mixin
。这个mixin
和Sass的一些Function
有机的配合在一起,可以同是给多个属性,或者多个属性值进行px
转em
的计算。在这里,将其mixin
名修改成了emCalc
,主要出发点,就是便于大家可以更好的记忆。别的不多说,直接看代码:
$base-font-size: 16 !default; @mixin emCalc($props,$sizes,$base:$base-font-size){ $values: (); $sublists: false; @each $s in $sizes { //循环列表中多个属性值,例如text-shadow属性 @if type-of($s) == list { $sublists: true; $vv: (); @each $ss in $s { $vv: append($vv,if(type-of($ss) == number, #{$ss / $base}em, $ss)); } $values: append($values,join((), $vv)); } @else { $values: append($values,if(type-of($s) == number, #{$s / $base}em, $s)); } } $value: join((), $values, if($sublists,comma,space)); @each $prop in $props {#{$prop}: $value} }
看上去是不是很复杂呀。其实也没有大家想像的那么复杂,你只需要把Sass中的Function
做一些了解,就好办了(我们后期会对Sass的Function
做详细的介绍)。
我们还是回到emCalc
上来。@mixin emCalc
主要功能是对元素属性的px
转换成em
单位,而这个mixin
具有一个更强大的功能,他可以同时给元素的多个属性,多个属性值或者同时多个属性多个属性值的px
单位转换成em
单位。不过在使用过程中,有一点也是非常重要的:emCalc
中给参数$base
传递的变量$base-font-size
只能是数值,不能带有任何单位,包括需要转换的属性值中也不能含有任何单位。
虽然emCalc
的定义看上去复杂,但实际使用是很简单,简单的来看一段代码:
//SCSS
.header {
@include emCalc(line-height, 30, 16); @include emCalc(width height, 125); @include emCalc(padding, 0 25, 16); @include emCalc(text-shadow, (#0d6e28 1 1) (#777 0 0 2), 16); @include emCalc(box-shadow, (inset 0 0 0 1 #2a9022) (inset 0 0 3 #459966), 16); } //CSS .header { line-height: 1.875em; width: 7.8125em; height: 7.8125em; padding: 0em 1.5625em; text-shadow: #0d6e28 0.0625em 0.0625em, #777777 0em 0em 0.125em; box-shadow: inset 0em 0em 0em 0.0625em #2a9022, inset 0em 0em 0.1875em #459966; }
定义em
计算函数
除了定义mixin
来实现px
向em
转换之外,我们还可以考虑Sass的另一个方案。在Sass中提供了函数function
的概念(如果没有了解过的同学,可以先看看《sass揭秘之@mixin,%,@function》),可以通过创建一个函数为实现这样的计算。
//如果不覆盖,设置一个默认字体大小(以像素为单位)
$base-font-size: 16px !default; //将px转换成em //当元素的父元素没有重置字体大小时,相对于16px计算,如果你要将12px转换成em时,可以写成`pxToem(12px)` //当元素的父元素重置了字体大小时,如24px,如果你要将12px转换成em时,可以写成`pxToem(12px,24px)` //另外你还可以不显式的写也单位`px` @function pxToem($target-size,$context:$base-font-size){ @if not unitless($target-size){//unitless(12)=>true,unitless(12px)=>false $target-size: strip-units($target-size);//去掉 $target-size单位 } @if not unitless($context){ $context: strip-units($context);//去掉 $context单位 } @return ($target-size / $context) * 1em; }
在pxToem()
函数设置了两个参数,$target-size
和$context
,并且给$context
传递了一个参数值$base-font-size
。而$base-font-size
是一个变量值,一般用来定义html
元素(根元素的字号,浏览器默认值为16px
)。很多地方,有介绍,为了方便px
与em
之里计算,一般设置为10px
。
而且在pxToem()
函数中,还使用了unitless()
函数,用来判断传$target-size
和$context
值是否带有单位。如果带有单位就为false
,否则就为true
:
unitless(12);//输出值为`true` unitless(12px);//输出值为`false`
这样一来,要是传给$target-size
和$context
是带有单位的值,我们就需要做一个判断,才能进入后面的计算,这个时候就需要添加一个not
来判断。
not unitless(12);//输出为`false`,跳过直接进入一行代码 not unitless(12px);//输出为`true`,做条件判断中程序处理
此时关键的时候又出现了,当传给$target-size
或者$context
的值带有单位时,我们需要做一件事情,将单位去掉。在上面的pxToem()
函数中使用了strip-units()
函数。可惜的是,在Sass中,并没有一个这样的函数,因此为了达到这个功能,需要自定义这个函数:
//去掉一个值的单位,如12px => 12
@function strip-units($number){ @return $number / ($number * 0 + 1); }
把strip-units()
和pxToem()
两个函数合并到一起,就实现了px
转em
的计算:
@function strip-units($number){ @return $number / ($number * 0 + 1); } $base-font-size: 16px !default; @function pxToem($target-size,$context:$base-font-size){ @if not unitless($target-size){ $target-size: strip-units($target-size); } @if not unitless($context){ $context: strip-units($context); } @return ($target-size / $context) * 1em; }
实际使用就非常简单了:
//SCSS
.header {
font-size: pxToem(12);
h1 {
font-size: pxToem(12,24); } } .footer { margin: pxToem(12px); h1 { margin: pxToem(12px,24px); } } //CSS .header { font-size: 0.75em; } .header h1 { font-size: 0.5em; } .footer { margin: 0.75em; } .footer h1 { margin: 0.5em; }
总结
今天主要和大家探讨了如何使用Sass完成px
转向em
的功能。从简单的mixin
开始,只为每个单独的属性服务,到复杂的mixin
,同时实现多个属性、属性值的转换计算。并且还通过定义函数来完成两者之间的转换计算。
不管使用mixin
还是function
来实现px
转em
的单位计算,都将回到他的最初原理。换句话说,要完全理解使用Sass完成px
转em
,就得理解清楚CSS中两者的转换关系。而且通过上面教程的学习,对 Sass的function
也会有一个初步的概念。希望大家喜欢这篇教程,如果您有更好的意见或看法,欢迎一起探讨。