将LESS CSS的mixin作为参数传递给另一个mixin

16

有没有办法将一个mixin或样式的声明作为输入参数传递给另一个mixin?

让我们看一个动画关键帧的例子。以下是如何在纯CSS中定义关键帧:

@-moz-keyframes some-name
{
    from { color: red; }
    to { color: blue; }
}

@-webkit-keyframes some-name
{
    from { color: red; }
    to { color: blue; }
}

@keyframes some-name
{
    from { color: red; }
    to { color: blue; }
}

想法是使用mixin简化这些声明,这样我们可以得到以下内容:
.keyframes(name, from, to)
{
    // here we need somehow to reproduce structure
    // that we have in an example above
}

// define one animation
.my-from() { color: red; }
.my-to() { color: blue; }
// the following won't work because you cannot pass mixin as a parameter
// in way I have here, so I am looking for a way to solve this problem
.keyframes('some-name', .my-from, .my-to);

// define another animation
.another-from() { font-size: 1em; }
.another-to() { font-size: 2em; }
.keyframes('another-name', .another-from, .another-to);

系统将拥有不同的模块,可以动态地附加到应用程序中,也可以移除。所以,请不要建议我使用@import,因为这不是情况。输出的CSS是根据模块和它们自己的LESS样式以及基本的LESS依赖项(如mixin库等)动态编译的。
注意:如果您知道一种传递类定义而不是mixin的方法,那么这对我很有帮助。在上面的示例中,它将是.my-form而不是.my-form()等。

你能更清楚地解释一下为什么你觉得不能直接使用mixin,以及你希望传递这个mixin给你带来什么吗?你是想使用mixin来定义一个@color变量和一个@font-size变量,以便在第二个mixin中使用吗?请具体说明,因为Petah的答案是正常使用它们的方式。 - ScottS
ScottS,请检查一下我对Petah帖子的回答。 - Alex M
4个回答

24

针对LESS 1.7.0+进行更新(更加简单

现在,我们可以通过1.7.0版本的更新和创建规则集的功能以及在设置@keyframes时使用变量来更加直接地完成这个过程。

现在,我们真正可以通过规则集通过参数传递mixin,或者直接传递属性字符串。请考虑以下内容:

LESS (使用 1.7)

.keyframes(@name, @from, @to) {
    @frames: {
        from { @from(); }
        to { @to(); }
    };
    @pre: -moz-keyframes;
    @-moz-keyframes @name
    {
       @frames();
    }

    @-webkit-keyframes @name
    {
       @frames();
    }

    @keyframes @name
    {
       @frames();
    }
}

.keyframes(testName, {color: red; .myMix(0);}, {color: blue; .myMix(1);});

.myMix(@value) {opacity: @value;}

请注意,我传递了一个属性设置和一个 mixin 调用,我的输出结果是:

CSS 输出

@-moz-keyframes testName {
  from {
    color: red;
    opacity: 0;
  }
  to {
    color: blue;
    opacity: 1;
  }
}
@-webkit-keyframes testName {
  from {
    color: red;
    opacity: 0;
  }
  to {
    color: blue;
    opacity: 1;
  }
}
@keyframes testName {
  from {
    color: red;
    opacity: 0;
  }
  to {
    color: blue;
    opacity: 1;
  }
}

注意规则集是如何以括号 {...} 形式传递的,并且通过 @from()@to() 调用(看起来很像 Mixin 调用)。我使用这些传递的规则集来设置另一个 @frames 的规则集,然后再调用它来填充关键帧定义。

更通用地说

在这里,我将一个私有的 Mixin 传递给另一个 Mixin,然后从那个另一个 Mixin 中调用它:

LESS

.someMixin(@class; @expectedMixin) {
    .@{class} {
      @expectedMixin();
      .myPrivateMix(0.6);
      test: 1;
    }
}

.someMixin(newClass; {.myClass;});

.myClass {
  .myPrivateMix(@value) {opacity: @value;}
}

CSS输出

.newClass {
  opacity: 0.6;
  test: 1;
}

保留以下信息以供参考。

更新(添加了 LESS 1.4.0+ 的支持)

哇,这确实需要一些工作,但我认为我有一些可以让您使用的东西。然而,它确实需要在模块中特别定义你的 mixins,具体来说就是使用 模式匹配。所以......

首先,定义你的模块 Mixins

请注意,模块 mixins 旨在在特定未来 mixin 中使用,使用了相同的 mixin 名称,但使用了不同的模式名称。这是使这个工作的关键。

// define one animation in a module
.from(my-from){ color: red; }
.to(my-to) { color: blue; }

// define one animation in another module
.from(another-from){ font-size: 1em; }
.to(another-to) { font-size: 2em; }

如果你还想在模块中获得单个mixin名称,你可以这样做:

// define one animation in a module
.my-from(){ color: red; }
.my-to() { color: blue; }

.from(my-from){ .my-from() }
.to(my-to) { .my-to() }   

// define one animation in another module
.another-from(){ font-size: 1em; }
.another-to() { font-size: 2em; }

.from(another-from){ .another-from() }
.to(another-to) { .another-to() }

这样可以通过调用直接的 mixin .my-from() 或通过模式匹配在稍后的 mixin 中访问单一的 .from() mixin 组,使其可变地访问。

接下来,定义您的Mixin

对于您的 @keyframes 示例,那非常困难。实际上,一个堆栈溢出答案 对我帮助很大,帮助我解决了应用 @name 的问题,由于它遵循 @keyframes 定义,因此在正常的 LESS 规则下不适用。应用 @name 的解决方案看起来很糟糕,但它有效。它确实有一个,可能是不幸的必要性,即还要定义一个选择器字符串来播放动画(因为它使用该字符串来帮助构建 keyframes 的最后一个 })。这种命名限制只适用于以 @ 开头的 css 字符串,例如 @keyframes 和可能的 @media

此外,由于我们在模块文件中使用了标准 mixin 名称,因此我们可以在新的 mixin 中始终访问它,同时通过模式匹配传递变量以选择该 mixin 的正确变体。所以我们得到:

LESS 1.3.3 或更低版本

// define mixin in mixin file

.keyframes(@selector, @name, @from, @to) {
    @newline: `"\n"`; // Newline
    .setVendor(@pre, @post, @vendor) {
        (~"@{pre}@@{vendor}keyframes @{name} {@{newline}from") {
            .from(@from); 
        }    
        to  { 
            .to(@to);
        }
       .Local(){}
       .Local() when (@post=1) {
           (~"}@{newline}@{selector}") {
              -moz-animation: @name;
              -webkit-animation: @name;
              -o-animation: @name;
              -ms-animation: @name;
              animation: @name;
           } 
       }    
       .Local;
    } 
    .setVendor(""            , 0,    "-moz-");
    .setVendor(~"}@{newline}", 0, "-webkit-");
    .setVendor(~"}@{newline}", 0,      "-o-");
    .setVendor(~"}@{newline}", 0,     "-ms-");
    .setVendor(~"}@{newline}", 1,         "");
}

LESS 1.4.0+

.keyframes(@selector, @name, @from, @to) {
    @newline: `"\n"`; // Newline
    .setVendor(@pre, @post, @vendor) {
        @frames: ~"@{pre}@@{vendor}keyframes @{name} {@{newline}from";
        @{frames} {
            .from(@from); 
        }    
        to  { 
            .to(@to);
        }
       .Local(){}
       .Local() when (@post=1) {
           @animationSector: ~"}@{newline}@{selector}";
           @{animationSector} {
              -moz-animation: @name;
              -webkit-animation: @name;
              -o-animation: @name;
              -ms-animation: @name;
              animation: @name;
           } 
       }    
       .Local;
    } 
    .setVendor(""            , 0,    "-moz-");
    .setVendor(~"}@{newline}", 0, "-webkit-");
    .setVendor(~"}@{newline}", 0,      "-o-");
    .setVendor(~"}@{newline}", 0,     "-ms-");
    .setVendor(~"}@{newline}", 1,         "");
}

现在调用您的Mixin

你可以给它一个自己的名字,并传递直接模式(所有的都是点[.]和引号)用于匹配模块mixins上的模式,但不要忘记您还需要一个选择器字符串(被引用)来正确地使用mixin:

.keyframes('.changeColor', some-name, my-from, my-to);
.keyframes('.changeFontSize', another-name, another-from, another-to);

让您得到所需的输出

@-moz-keyframes some-name {
from {
  color: red;
}
to {
  color: blue;
}
}
@-webkit-keyframes some-name {
from {
  color: red;
}
to {
  color: blue;
}
}
@-o-keyframes some-name {
from {
  color: red;
}
to {
  color: blue;
}
}
@-ms-keyframes some-name {
from {
  color: red;
}
to {
  color: blue;
}
}
@keyframes some-name {
from {
  color: red;
}
to {
  color: blue;
}
}
.changeColor {
  -moz-animation: some-name;
  -webkit-animation: some-name;
  -o-animation: some-name;
  -ms-animation: some-name;
  animation: some-name;
}
@-moz-keyframes another-name {
from {
  font-size: 1em;
}
to {
  font-size: 2em;
}
}
@-webkit-keyframes another-name {
from {
  font-size: 1em;
}
to {
  font-size: 2em;
}
}
@-o-keyframes another-name {
from {
  font-size: 1em;
}
to {
  font-size: 2em;
}
}
@-ms-keyframes another-name {
from {
  font-size: 1em;
}
to {
  font-size: 2em;
}
}
@keyframes another-name {
from {
  font-size: 1em;
}
to {
  font-size: 2em;
}
}
.changeFontSize {
  -moz-animation: another-name
  -webkit-animation: another-name;
  -o-animation: another-name;
  -ms-animation: another-name;
  animation: another-name;
}

如果作为参数传递的mixin应该使用参数调用怎么办?使用@to("SomeParam");似乎不起作用。 - Sebastien Lorber
@SebastienLorber:重要的是要记住@to()是一个规则集调用,而不是mixin,因此它不需要参数。要将参数传递到规则集中,请查看示例,其中mixin调用本身作为规则集的一部分被传递(例如.myMix(0)),该mixin被定义为.myMix(@value) {opacity: @value;}。我希望这可以帮助您获得您所寻求的内容。 - ScottS

1

简化

我刚刚简化了ScottS的方法,将@keframes-animation分开:

.keyframes(@name, @from, @to) {
    @newline: `"\n"`;
    .Local(@x){};
    .Local(@x) when (@x="") {(~"}@{newline}/*"){a:a}/**/};

    .setVendor(@pre, @vendor) {
        (~"@{pre}@@{vendor}keyframes @{name} {@{newline}from") {
            .from(@from);
        }
        to {
            .to(@to);
        }
        .Local(@vendor);
    }
    .setVendor(""            , "-webkit-");
    .setVendor(~"}@{newline}",    "-moz-");
    .setVendor(~"}@{newline}",      "-o-");
    .setVendor(~"}@{newline}",         "");
}

.animation(...) {
  -webkit-animation: @arguments;
     -moz-animation: @arguments;
       -o-animation: @arguments;
          animation: @arguments;
}

使用:

.from(a1-from){ width: 10px; }
.to(a1-to) { width: 20px; }
.keyframes(a1-animation, a1-from, a1-to);


.selector {
    // some other css
    .animation(a1-animation 1s infinite linear);
}

输出:

@-webkit-keyframes a1-animation {
from {
  width: 10px;
}
to {
  width: 20px;
}
}
@-moz-keyframes a1-animation {
from {
  width: 10px;
}
to {
  width: 20px;
}
}
@-o-keyframes a1-animation {
from {
  width: 10px;
}
to {
  width: 20px;
}
}
@keyframes a1-animation {
from {
  width: 10px;
}
to {
  width: 20px;
}
}
/* {
  a: a;
}
/**/


.selector {
  // some other css
  -webkit-animation: a1-animation 1s infinite linear;
  -moz-animation: a1-animation 1s infinite linear;
  -o-animation: a1-animation 1s infinite linear;
  animation: a1-animation 1s infinite linear;
}

小问题:

现在动画已经与@keyframes分离,但我们需要付出代价。有一个讨厌的问题:

/* {
  a: a;
}
/**/

但这不应该是一个问题 - 可能我们所有人都会通过各种压缩器来压缩CSS文件,这些压缩器会删除注释。

1
您还可以使用我的解决方案生成CSS关键帧:https://github.com/thybzi/keyframes 特点:
  • 跨浏览器的关键帧生成(Firefox 5+,Chrome 3+,Safari 4+,Opera 12+,IE 10+)
  • 每个keyframes规则中最多有16个时间点(如果需要,数字可以轻松增加)
  • 可以使用mixin、变量和函数来为时间点设置样式
  • 关键帧是单独创建的,与animation规则分开,因此:
    • 多个animation规则可以使用相同的关键帧,但定时、重复等值不同
    • 多个动画可以在同一个animation规则中使用
    • 动画可以应用于(而不是创建!)任何父选择器中
  • 轻量级且(几乎)整洁的LESS代码
基本用法:
// Preparing styles for animation points
.keyframes-item(fadeIn, 0%) {
    opacity: 0;
}
.keyframes-item(fadeIn, 100%) {
    opacity: 1;
}
// Generating keyframes
.keyframes(fadeIn);

// Applying animation to fade-in block in 1.5 seconds
.myBlock {
    .animation(fadeIn 1.5s);
}

0

这不是你使用mixin的方式。

你应该做类似于以下的事情:

.mixin-one { ... }
.mixin-two { ... }
.target-style {
    .mixin-one;
    .mixin-two;
    font-family: 'Comic Sans MS';
    color: magenta;
}

ScottS,这并不是我的情况。我手头的东西是:项目包含不同的模块,每个模块都有自己的样式(LESS),所有这些模块的样式都使用全局 mixins 库。因此,没有 @import ,甚至 mixin 库也不知道这些外部模块。一切都是动态发生的,根据这些模块构建输出 CSS 等等,而不是像 LESS 文件 + js 那样直接使用 LESS 进行编译。 - Alex M
@JoAsakura(顺便说一下-不要忘记在我的名字前面使用“@”,这样它会提醒我你在评论中提到了我)。如果mixin库对模块一无所知,那么它们的mixin怎么可能被mixin库以任何方式使用呢?而且我仍然不清楚你想如何使用mixin的模块信息。 - ScottS
@ScottS,混合库不需要知道它被使用的位置,混合是一组“辅助函数”,对吧?那么,为什么您认为混合需要知道模块的情况? - Alex M
@ScottS, @Petah,现在说一下我的问题:最简单的例子是动画关键帧。您可以在模块中使用@-webkit-keyframes@-moz-keyframes等定义它们。但我希望通过mixin定义此内容,因此我只能在模块中定义"from"和"to",然后将它们作为参数传递到定义关键帧结构的mixin内部。这就是我要做的最简单的示例。 - Alex M
@JoAsakura,太好了,真实有用的信息。我建议你考虑使用SCSS/Compass,因为它更擅长处理这种高级的东西。http://compass-style.org/ - Petah
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接