原文:https://www.sitepoint.com/sass-mixins-kickstart-project/
Mixins很好用,就像函数一样,可以输出CSS。Sass的官方文档是这样描述的:
Mixins 允许自己定义样式,这些样式可以在全局样式表里重用,而不用去借助一些无语义的类,比如.float-left。Mixins可以包含完整的CSS样式规则和其他Sass中的特性规则等。mixin还可以接收参数,不同的参数值将产生不同的样式规则。
Mixins使用起来非常方便,特别是在大型项目中。在你最近的项目中,你需要定义多少次宽度和高度这两个属性值?为了实现一个CSS三角形,你需要搜索谷歌多少次?或者当设置CSS定位时,你是不是希望有一个快捷方式,可以同时一起设置top,right,bottom,left这些属性?
你可以使用mixins解决这些问题。更爽的是:我已经替你写好这些常用的mixins了。那让我们开始吧。
Sizing Mixin
一个 size()
mixin 同时可以定义宽度和高度。第一个参数是宽度,第二个是高度。如果高度参数没有设置,那么高度默认和宽度一样。
@mixin size($width, $height: $width) {
width: $width;
height: $height;
}
非常简单。
用法示例
Sass
.element {
@include size(100%);
}
.other-element {
@include size(100%, 1px);
}
CSS 输出:
.element {
width: 100%;
height: 100%;
}
.other-element {
width: 100%;
height: 1px;
}
Positioning Mixin
我认为CSS最缺少一个简写,比如top,right,bottom,left这些偏移量属性的简写方式。CSS自己已经有了一些可以简写的属性:padding
,margin
,background
,甚至text-decoration
!但是没有偏移量属性的简写,所以我开始自己写。
受到Stylus语法的启发:
absolute: top 0 left 1em
不幸的是,Sass没有提供一个transparent mixin这样的特性。但是我们可以这样做:
@include absolute(top 0 left 1em);
这种方式看起来还不错。很明显,relative()
和fixed()
和上边是一样的。
我之前写过一篇博客,里面详细介绍了如何实现这个mixin。所以这里我就不再详细去说了。不过主要思想是为这个mixin提供一个列表值。如果在这个列表中存在top,right,bottom,left这些属性,那么这些属性的值就跟在它们后边。不过要保证这些值是有效的。
@mixin position($position, $args) {
@each $o in top right bottom left {
$i: index($args, $o);
@if $i and $i + 1< = length($args) and type-of(nth($args, $i + 1)) == number {
#{$o}: nth($args, $i + 1);
}
}
position: $position;
}
@mixin absolute($args) {
@include position("absolute", $args);
}
@mixin fixed($args) {
@include position("fixed", $args);
}
@mixin relative($args) {
@include position("relative", $args);
}
用法示例
Sass:
.element {
@include absolute(top 0 left 1em);
}
.other-element {
@include fixed(top 1em left "WAT? A STRING?!" right 10% bottom)
}
CSS 输出:
.element {
position: absolute;
top: 0;
left: 1em;
}
.other-element {
position: fixed;
top: 1em;
right: 10%;
}
Vendor Prefix Mixin
当使用CSS3的一些属性时,需要加上各种浏览器厂商提供的属性前缀。比如-webkit-
,-moz-
等。自己手动加前缀,太繁琐。不过Compass和Bourbon这些框架已经为我们提供了这些前缀集合。比如@include box-sizing()
。
我一直在使用Compass,但是现在我决定放弃它。主要原因是,它提供的东西,我在项目中只能使用一小部分。放弃使用之后,编译时间也缩短了。当我需要前缀mixin时,我可以自己编写(更新到Sass 3.3)。
Sass 3.2 版本
做法很简单。第一个参数是属性。第二个参数是属性值。第三个参数是可选的,表示可以使用的前缀列表。默认值是全部前缀。
@mixin prefix($property, $value, $vendors: webkit moz ms o) {
@if $vendors {
@each $vendor in $vendors {
#{"-" + $vendor + "-" + $property}: #{$value};
}
}
#{$property}: #{$value};
}
用法示例
Sass:
.element {
@include prefix(transform, rotate(42deg), webkit ms);
}
输出:
.element {
-webkit-transform: rotate(42deg);
-ms-transform: rotate(42deg);
transform: rotate(42deg);
}
Sass 3.3 版本
Sass 3.3
版本更好,因为它可以做到一次设置多个属性的前缀,使用map
这个数据类型。现在你只需要一个map参数,它的键是属性名,值是属性值。
@mixin prefix($map, $vendors: webkit moz ms o) {
@each $prop, $value in $map {
@if $vendors {
@each $vendor in $vendors {
#{"-" + $vendor + "-" + $prop}: #{$value};
}
}
// Dump regular property anyway
#{$prop}: #{$value};
}
}
用法示例
Sass:
.element {
@include prefix((transform: translate(-50%, -50%)), webkit ms);
}
.other-element {
@include prefix((
column-count: 3,
column-gap: 1em,
column-rule: 1px solid silver,
column-width: 20em
)), webkit moz);
}
CSS 输出:
.element {
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.other-element {
-webkit-column-count: 3;
-moz-column-count: 3;
column-count: 3;
-webkit-column-gap: 1em;
-moz-column-gap: 1em;
column-gap: 1em;
-webkit-column-rule: 1px solid silver;
-moz-column-rule: 1px solid silver;
column-rule: 1px solid silver;
-webkit-column-width: 20em;
-moz-column-width: 20em;
column-width: 20em;
}
进一步优化
可以在其他mixins里边使用prefix()
,像下边这样:
@mixin transform($value) {
@include prefix(transform, $value, webkit ms);
}
@mixin column-count($value) {
@include prefix(column-count, $value, webkit moz);
}
用法示例
.element {
@include transform(rotate(42deg));
}
CSS 输出:
.element {
-webkit-transform: rotate(42deg);
-ms-transform: rotate(42deg);
transform: rotate(42deg);
}
Opposite Direction Mixin
如果你是Compass的用户,你一定对opposite-direction
这个函数很熟悉。这个函数不是mixin。因为Compass是由Ruby写的。不过很容易用Sass实现这个同样功能的mixin。下边的示例,我使用了Sass 3.3版本,不过也很容易调整改写成Sass 3.2版本。
在我写的函数中,你可以传递一个方向列表。所以你可以从反方向(top right
)得到bottom left
。当你在处理background-position
时,这个会非常有用。
@function opposite-direction($directions) {
$opposite-directions: ();
$direction-map: (
'top': 'bottom',
'right': 'left',
'bottom': 'top',
'left': 'right',
'ltr': 'rtl',
'rtl': 'ltr'
);
@each $direction in $directions {
$opposite-direction: map-get($direction-map, $direction);
@if $opposite-direction != null {
$opposite-directions: append($opposite-directions, #{$opposite-direction});
}
@else {
@warn "No opposite direction can be found for `#{$direction}`.";
}
}
@return $opposite-directions;
}
用法示例
$direction: opposite-direction(top);
// bottom
$other-direction: opposite-direction(bottom left);
// top right
Breakpoint Handler Mixin
如果你之前在做响应式设计的话,那么你会知道处理几种不同的媒体查询会很麻烦。现在有一个好的做法:用变量把不同的断点值存储起来。每次使用的时候只需要只用对应的变量就行了。
如果想更进一步的话,你可以去命名每个断点。这样的话,断点处理mixin就只根据这个断点名字就可以输出对应的断点查询。
还可以再进行优化,那就是把这些命名和它对应的断点
存储在一个map中。代码如下:
$breakpoints: (
'tiny': ( max-width: 767px ),
'small': ( min-width: 768px ),
'medium': ( min-width: 992px ),
'large': ( min-width: 1200px ),
'custom': ( min-height: 40em )
);
@mixin breakpoint($name) {
@if map-has-key($breakpoints, $name) {
@media #{inspect(map-get($breakpoints, $name))} {
@content;
}
}
@else {
@warn "Couldn't find a breakpoint named `#{$name}`.";
}
}
如果breakpoint
mixin中传入的字符串值和$breakpoints
这个map中的某个键值匹配,那么这个mixin会打开一个@media指令,然后使用inspect
函数(来自Sass 3.3)输出这个map。当使用inspect((key:value))
时,'(key:value)`这个字符串会被输出到样式表中,相当于字符串化操作。
如果传入的字符串参数没有匹配任何一个断点map的键值,那么@warn
这个指令会输出警告信息,在控制台里可以看到。
用法示例
Sass:
.element {
color: red;
@include breakpoint(medium) {
color: blue;
}
}
CSS 输出:
.element {
color: red;
}
@media (min-width: 992px) {
.element {
color: blue;
}
}
最后
我相信这是一个好的开始。这些工具可以让你在下个项目中,减少编写CSS花费的时间。