原文: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自己已经有了一些可以简写的属性:paddingmarginbackground,甚至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-等。自己手动加前缀,太繁琐。不过CompassBourbon这些框架已经为我们提供了这些前缀集合。比如@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}`.";
  }
}

如果breakpointmixin中传入的字符串值和$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花费的时间。


前端知否
714 声望49 粉丝

to be is to do