较少 - 参数mixin与简写数字和/或字符串值,具有rem和px后备

时间:2014-08-01 15:38:04

标签: css less mixins

使用Less,我尝试输出属性的输出css组合速记值,同时检测作为变量传递的值的类型,具体取决于作为变量传递的值的数量。

  • 一个值@v1
  • 两个值@v1@v2
  • 四个值@v1@v2@v3@v4

对于检测到的每个变量,检查is是否是大于0的数字,如果是,则输出每个属性值的rem和px值,因此编译的css可能类似于:

.demobox{
  border-width: 10px 20px 0 0;
  border-width: 1rem 2rem 0 0;
  padding: 0 50px;
  padding: 0 2rem;
  margin: 20px auto;
  margin: 2rem auto;
  font-size: 16px;
  font-size: 1.6rem;
}

如果声明的值不超过两个,这很容易解决,但如果我需要四个值,嵌套的mixins和guard的数量会变得非常复杂。

选择rems的原因是与ems相比易于实现,并且可以防止记住必须将字体大小重置为每个父项。此外,旧的设备支持问题对于rems和ems都具有可比性,因此我认为px后备效果最佳。

下面' S'到目前为止我所在的code pen,但我会尽可能地尝试解释我的工作。

有没有办法简化这个过程?我是新手,少了。我想有一种更简单的方法来检测

  • 声明值的数量
  • 每个值的类型
  • 如果数值需要rem和px值回退

由于

我有一个参数化mixin,它输出rem值和px后备值以及声明的属性

@root-font-size: 10;

.rem(@property, @v1) when (@v1 = @v1){
    @px1: @v1 * @root-font-size;
    @{property}:  ~'@{px1}px';
    @{property}: ~'@{v1}rem';
}

这非常适合快速生成set css属性的rem和pixel fallback

/* call the rem mixin */
.demobox {
    .rem(margin, 2);
}
/* output */
.demobox {
    margin: 20px;
    margin: 2rem;
}

一个非常有用的案例是使用回退生​​成font-sizeline-height

/* call the font-classes mixin */
.font-classes(@fontsize, @lineheight) {
    .rem(font-size, @fontsize);
    .rem(line-height, @lineheight);
}
/* output */
.demobox {
    font-size: 16px;
    font-size: 1.6rem;
    line-height: 24px;
    line-height: 2.4rem;
}

到目前为止,我可以为单个值输出rem和pixel后备,并将.rem mixin嵌套在另一个mixin中。

目前,如果我想传递多个值,我必须为每个所需的值调用.rem mixin:

/* call the rem mixin */
.demobox {
    .rem(margin-left, 2);
    .rem(margin-right, 2);
}
/* output */
.demobox {
    margin-left: 20px;
    margin-left: 2rem;
    margin-right: 20px;
    margin-right: 2rem;
}

理想情况下,我希望能够将带有属性的两个或四个值传递给.rem mixin。

.demobox {
    .rem(margin, 2, 1);
}

正如我之前提到的,对于两个值并不是那么困难,并检查值类型是什么。这需要使用版本.rem mixin并应用gaurd来检查@v2是否已声明。

这会触发嵌套的.rem-two混合。

// when there are two values
.rem(@property, @v1, @v2) when (@v1 = @v1) and (@v2 = @v2){
    .rem-two(@property, @v1, @v2);
}

.rem-two有三个版本,每个版本的警卫都不同。

检测@v1@v2是否都是大于0的数字

.rem-two(@property, @v1, @v2) when (@v1 = @v1) and not (@v1 = 0) and (isnumber(@v1)) and (@v2 = @v2) and not (@v2 = 0) and (isnumber(@v2)) {
    @px1: @v1 * @root-font-size;
    @px2: @v2 * @root-font-size;
    @{property}:  ~'@{px1}px @{px2}px';
    @{property}: ~'@{v1}rem @{v2}rem';
}

检测@v1@v2是否都是大于0的数字

.rem-two(@property, @v1, @v2) when (@v1 = @v1) and not (@v1 = 0) and (isnumber(@v1)) and (@v2 = 0), not (isnumber(@v2)){
    @px1: @v1 * @root-font-size;
    @{property}:  ~'@{px1}px @{v2}';
    @{property}: ~'@{v2}rem @{v2}';
}

/* call the rem mixin */
.demobox {
    .rem(margin, 2, 1);
}
/* outputs */
.demobox {
    margin: 10px 20px;
    margin: 1rem 2rem;
}

检测两个@v1是否大于0且@v2的值是0还是数字

' @ px2'不需要像素回退,因此将其删除。

.rem-two(@property, @v1, @v2) when (@v1 = @v1) and not (@v1 = 0) and (isnumber(@v1)) and (@v2 = 0), not (isnumber(@v2)){
    @px1: @v1 * @root-font-size;
    @{property}:  ~'@{px1}px @{v2}';
    @{property}: ~'@{v2}rem @{v2}';
}
/* call the rem mixin */
.demobox {
    .rem(margin, 2, auto);
}
/* outputs */
.demobox {
    margin: 10px auto;
    margin: 1rem auto;
}

检测@v1是否为值0或不是数字,@v2是大于0的数字

' @ px1'不需要像素回退,因此将其删除。

.rem-two(@property, @v1, @v2) when (@v1 = 0), not (isnumber(@v1)) and (@v2 = @v2) and not (@v2 = 0) and (isnumber(@v2)){
    @px2: @v2 * @root-font-size;
    @{property}:  ~'@{v1} @{px2}px';
    @{property}: ~'@{v1} @{v2}rem';
}
/* call the rem mixin */
.demobox {
    .rem(margin, 0, 20);
}
/* outputs */
.demobox {
    margin: 0 20px;
    margin: 0 2rem;
}

因为mixin的这两个值版本只有3种可能性很容易解决这个问题,但是有3或4个值,嵌套的mixins和guard的数量会扩展到所有可能性。

作为附加示例。 我已将.rem mixin重新调整为输出pxremborder-radius值与供应商前缀,以确保它只处理数值,我有警卫检查是否传递的值是一个大于0的数字

.prefix(@property, @v1) when (isnumber(@v1)) and (@v1 > 0) {
    @px1: @v1 * @root-font-size;
    -webkit-@{property}: ~'@{px1}px';
    -moz-@{property}: ~'@{px1}px';
    @{property}: ~'@{px1}px';
    -webkit-@{property}: ~'@{v1}rem';
    -moz-@{property}: ~'@{v1}rem';
    @{property}: ~'@{v1}rem';
}
/* call prefix mixin */
.demobox{
    .prefix(border-radius,5);
}
/* output */
.demobox{
    -webkit-border-radius: 50px;
    -moz-border-radius: 50px;
    border-radius: 50px;
    -webkit-border-radius: 5rem;
    -moz-border-radius: 5rem;
}

我可以交换警卫来检查该值是否为0(因此我可以重置一个属性)或者它是否是一个数字以外的其他值,例如输出前缀框大小。

我问not (isnumber(@v1))而不是(isstring(@v1))的警卫的原因是,我不必添加单引号“' border-box&#39 ;

.prefix(@property, @v1) when (@v1 = 0), not (isnumber(@v1)) {
    -webkit-@{property}: ~'@{v1}';
    -moz-@{property}: ~'@{v1}';
    @{property}: ~'@{v1}';
}
/* call prefix mixin */
.demobox {
    .prefix(box-sizing, border-box);
}
/* output */
.demobox {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}

1 个答案:

答案 0 :(得分:0)

如果您将传递的参数视为一个数组(简化的impl。,在您的代码段中有太多要求覆盖示例),您可以简化一些事情,少于1.7。 x 或更高:

// usage

@root-font-size: 10;

div {
    .rem(border-radius, 1, margin, 2 auto, padding, 4 5 6 inherit);
}

// impl:

.rem-value_(@p, @v, @u) when (isnumber(@v)) {
    @{p}+_: (@v * @u);
}

.rem-value_(@p, @v, @u) when (default()) {
    @{p}+_: @v;
}

.rem(@args...) {
    .i; .i(@i: length(@args)) when (@i > 0) {
        .i((@i - 2));
        @property: extract(@args, (@i - 1));
        @values:   extract(@args,  @i);
        .j(@property, @values, (1px * @root-font-size));
        .j(~'@{property} ', @values, 1rem); // have to use ~'@{property} ' hack to isolate rem and px properties;
    }
    .j(@p, @v, @u, @j: length(@v)) when (@j > 0) {
        .j(@p, @v, @u, (@j - 1));
        .rem-value_(@p, extract(@v, @j), @u);
    }
} 

循环非常可怕但是输出mixin非常透明且可完全自定义(如果需要,可以添加更多条件)。