Sass基础——PXtoEMMixin和@function

CSS单位是一个很意思的东西,到目前为止,CSS的单位不仅仅局限于em,px,pt,com,in...,还出现了新的单位,比如remvwvhvminvmax等等。在CSS-trick有对这些单位进行描述(可以点击这里阅读中文)。在这么多的单位中,其中pxem两者的互转是最令同学们头痛的。简值是一言难尽,理不清呀!

曾经在《CSS中强大的EM》一文中详细的介绍了px转成em的公式与使用细节,但只是仅仅看文字描述还是让人晕晕的,最好是动手写写。如果你想了解更多有关于pxem相关知识,建议您仔细阅读一下下面的文章:

如果你理清楚了pxem之间的关系,那么你就可以使用Em Calculator工具,在线将px转成em

看到上面的在线生成工具,我们应该回到今天我们要讨论的主题,如何使用Sass实现px转成em。简单点说,使用Sass来实现"Em Calculator"工具功能。

CSS中的pxem

在CSS中pxem,大家都有相当深的体会,特别是对于一些涉及这方面不深的同学,往往都被这两者折磨死了。如果您掌握了其中的绝巧,你就不会觉得复杂了。在《CSS中强大的EM》虽然在文中详细介绍了两者的转换的详细细节,文章相对蛮长的,或许有很多同学不喜欢阅读,有的网友对此做过一些总结。

综合以上所述,我们可以简单的理解为:“默认1em=16px,而font-size使用em为单位时,是相对于其父元素(或祖先元素)的font-size;而元素其他属性(有关于box module,例如padding,border,width,height,margin等)使用em为单位时,是相对于元素本身的font-size值来计算。

知道如何使用公式将px转换成em,但在实际使用中,给我们的工作带来很多不便之处。特别是改变基本字体的时候,那你的噩梦就即将来了。或许你会说,我可以借助Em Calculator工具来完成。但这始终不是解决问题的最佳方案。

Sass中的pxem

我想使用Sass同学都会定义很多mixins运用于不同的项目中。我现在就在做这样的一件事情,将很多常用的公共样式模块抽取出来,分别定义成mixins,并且适用于每一个项目之中。今天将要做的也是类似于这样的一件事情。

有关于Sass中的pxem的代码片段到处可见,此处我们就不在纠结谁的代码优秀,谁的代码拙。我想我们应该了解他是怎么定义的,并且如何使用?

Sass的pxem要点

使用Sass来实现pxem的计算,有几点需要注意:

pxem的计算

使用Sass来完成px转成em,其中第一步,也是非常关键的一步,需要明白两者之间怎么进行换算。前面我们也回忆了CSS中的pxem转换的计算。那么其原理同样可以运用到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实现了具体数字计算到变量计算的转换,也实现了最初示例所示的功能。但实际使用之中,pxem并不仅仅用在font-size属性,还有其他能使用长度单位的属性上。那上面的mixin离我们的目标还很遥远,我们应该继续的思考。

修改pxem的mixin

在GitHub上找到了一个功能强大的mixin,这个mixinDan Adams两年前写的一个关于pxemmixin。这个mixin和Sass的一些Function有机的配合在一起,可以同是给多个属性,或者多个属性值进行pxem的计算。在这里,将其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来实现pxem转换之外,我们还可以考虑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)。很多地方,有介绍,为了方便pxem之里计算,一般设置为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()两个函数合并到一起,就实现了pxem的计算:

@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来实现pxem的单位计算,都将回到他的最初原理。换句话说,要完全理解使用Sass完成pxem,就得理解清楚CSS中两者的转换关系。而且通过上面教程的学习,对 Sass的function也会有一个初步的概念。希望大家喜欢这篇教程,如果您有更好的意见或看法,欢迎一起探讨。

出处:http://www.w3cplus.com/preprocessor/sass-px-to-em-with-mixin-and-function.html