МЕНЬШЕ CSS Передать миксин в качестве параметра другому миксину

Есть ли способ передать объявление одного миксина или стиля другому миксину в качестве входного параметра?

Давайте рассмотрим пример с ключевыми кадрами анимации. Ниже показано, как мы определяем ключевые кадры в чистом 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; }
}

Идея состоит в том, чтобы упростить эти объявления с помощью примесей, поэтому у нас может быть что-то вроде следующего:

.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-зависимости, такие как библиотека миксинов и т. д.

Примечание: это сработает для меня, если вы знаете способ передать определение класса вместо миксина. В приведенном выше примере это будет .my-from вместо .my-from() и т. д.


person Alex M    schedule 18.07.2012    source источник
comment
Не могли бы вы пояснить, почему вы чувствуете, что не можете использовать миксин напрямую, а также что вы ожидаете от передачи этого миксина? Вы пытаетесь использовать миксин, чтобы определить, скажем, переменную @color и переменную @font-size для использования во втором миксине? Пожалуйста, будьте более конкретными, так как ответ Петаха был бы нормальным способом их использования.   -  person ScottS    schedule 19.07.2012
comment
ScottS, пожалуйста, проверьте мой ответ на сообщение Петаха.   -  person Alex M    schedule 19.07.2012


Ответы (4)


ОБНОВЛЕНО для LESS 1.7.0+ (НАМНОГО проще)

Теперь мы можем сделать это гораздо более прямолинейно благодаря обновлению 1.7.0 и возможности создавать наборы правил и использовать переменные в настройке @keyframes.

Теперь мы действительно можем передать миксин через параметр с помощью набора правил, или мы можем передать сами свойства. Итак, подумайте об этом:

МЕНЬШЕ (используя 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;}

Обратите внимание, что я передаю как настройку свойства, так и вызов миксина, и мой вывод:

Вывод 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() (очень похоже на вызов примеси). Я использую эти переданные наборы правил, чтобы установить другой набор правил @frames, который затем сам вызывается для заполнения определений ключевых кадров.

Более общие сведения

Здесь я передаю приватный миксин другому миксину, а затем вызываю его из этого другого миксина:

МЕНЬШЕ

.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+)

Вау, это потребовало некоторых усилий, но я думаю, что у меня есть кое-что, с чем вы можете поработать. Тем не менее, это требует специального определения ваших миксинов в ваших модулях, в частности, с использованием соответствие шаблону. Так...

Во-первых, определите миксины вашего модуля

Обратите внимание, что примеси модуля, предназначенные для использования в конкретном будущем примеси, определяются с тем же именем примеси, но с другим именем шаблона. Это было ключом к созданию этой работы.

// 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; }

Если вам также нужны отдельные имена миксинов в модулях, вы сможете сделать это:

// 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() }

Это должно позволить вызвать либо прямой миксин .my-from(), либо сделать его переменно доступным в более поздних миксинах, которые обращаются к единственной группе миксинов .from() через сопоставление с образцом.

Затем определите свой миксин

Для вашего примера @keyframes это было чрезвычайно сложно. Фактически, ответ на переполнение стека было очень важно помочь мне решить проблему с применением @name, которая не применялась в соответствии с обычными правилами LESS из-за того, что она соответствовала определению @keyframes. Решение применить @name выглядит неприятно, но оно работает. Возможно, у него есть досадная необходимость также определять строку селектора для воспроизведения анимации (поскольку он использует эту строку для построения последних } ключевых кадров). Это ограничение именования справедливо только для строк css, начинающихся с @, таких как @keyframes и, возможно, @media.

Кроме того, поскольку у нас есть стандартное имя примеси, используемое в наших файлах модуля, мы можем последовательно получить к нему доступ в нашем новом примеси, в то же время передавая переменную для выбора правильного варианта этого примеси через совпадение по образцу. Итак, мы получаем:

МЕНЬШЕ 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,         "");
}

МЕНЬШЕ 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,         "");
}

Теперь назовите свой миксин

Вы можете дать ему свое собственное имя и просто передать прямой шаблон (все без точки [.] и без кавычек) для совпадения шаблона в миксинах модуля, но не забывайте, что вам также нужна строка селектора (которая заключена в кавычки), чтобы миксин работал правильно:

.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;
}
person ScottS    schedule 21.07.2012
comment
А что, если миксин, переданный в качестве параметра, должен вызываться с параметрами? Использование @to("SomeParam"); не работает - person Sebastien Lorber; 02.11.2015
comment
@SebastienLorber: важно помнить, что @to() — это вызов набора правил, а не mixin, поэтому он не принимает параметры. Чтобы передать параметры в набор правил, посмотрите на пример, где сам вызов миксина передается как часть набора правил (например, .myMix(0)), который был определен как .myMix(@value) {opacity: @value;}. Я надеюсь, что это поможет вам получить то, что вы ищете. - person ScottS; 02.11.2015

Упрощение

Я немного упростил подход Скотта, отделив @keframes от -анимации:

.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 через любые виды минификаторов, которые вырезают комментарии.

person ulfryk    schedule 13.02.2013

Вы также можете использовать мое решение для создания ключевых кадров CSS: https://github.com/thybzi/keyframes

Возможности:

  • Кроссбраузерное создание ключевых кадров (Firefox 5+, Chrome 3+, Safari 4+, Opera 12+, IE 10+)
  • До 16 моментов времени в каждом правиле keyframes (при необходимости это число можно легко увеличить)
  • Миксины, переменные и функции можно использовать для стилизации временных точек.
  • Keyframes are created separately from animation rules, so:
    • multiple animation rules can use the same keyframe with different values for timing, repeating, etc,
    • несколько анимаций могут использоваться в одном правиле 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);
}
person thybzi    schedule 29.11.2013

На самом деле это не то, как вы будете использовать миксины.

Вы должны сделать что-то вроде:

.mixin-one { ... }
.mixin-two { ... }
.target-style {
    .mixin-one;
    .mixin-two;
    font-family: 'Comic Sans MS';
    color: magenta;
}
person Petah    schedule 18.07.2012
comment
Да, я знаю, что могу сделать так, но моя проблема сложнее. В моей системе есть общие правила примесей, такие как библиотека справочных оболочек для материалов css3, и эта библиотека используется различными модулями, и идея состоит в том, чтобы передавать информацию из этих модулей в библиотеку примесей для создания выходного css. Как видите, библиотека примесей не может знать о примесях, объявленных внутри разных внешних модулей. Итак, мне нужен способ передать эту информацию в мою библиотеку миксинов. - person Alex M; 19.07.2012
comment
@JoAsakura - почему вы не можете просто @import (см. здесь) свои модули в библиотеку миксинов, а затем все переменные и примеси в них будут доступны для основного файла. После импорта используйте описанный выше метод Петаха. - person ScottS; 19.07.2012
comment
ScottS, это не моя ситуация. У меня следующее: проект содержит разные модули, и каждый модуль имеет свои собственные стили (МЕНЬШЕ), все эти стили модулей используют глобальную библиотеку миксинов. Таким образом, нет @import или даже возможности, чтобы библиотека миксинов знала об этих внешних модулях. Все происходит динамически, я создаю выходной CSS на основе этих модулей и т. Д., Это не прямое использование LESS, такого как LESS file + js, для его компиляции. - person Alex M; 19.07.2012
comment
@JoAsakura (кстати, не забудьте использовать @ перед моим именем, чтобы предупредить меня, что вы упомянули меня в комментарии). Если библиотека миксинов ничего не знает о модулях, то как миксины из них могут каким-либо образом использоваться библиотекой миксинов? И мне до сих пор неясно, как именно вы хотите, чтобы информация о модуле использовалась миксином. - person ScottS; 19.07.2012
comment
@JoAsakura Я согласен, нам нужно больше информации, чтобы помочь вам должным образом. Дайте вам полный вариант использования с примерами. - person Petah; 20.07.2012
comment
@ScottS, библиотеке Mixins не нужно ничего знать о том, где она используется, миксины - это просто набор вспомогательных функций, верно? Итак, почему вы думаете, что миксины должны знать о модулях? - person Alex M; 20.07.2012
comment
@ScottS, @Petah, теперь о моей проблеме: самый простой пример - это ключевые кадры анимации. Вы можете определить их все локально в модуле, используя @-webkit-keyframes, @-moz-keyframes и т. д. Но я хочу, чтобы миксины определяли эти вещи, поэтому я могу определить только from и to внутри модуля и передать их в качестве аргументов внутри миксина, который определяет структуру ключевых кадров. Это самый простой пример того, что я собираюсь сделать. - person Alex M; 20.07.2012
comment
@JoAsakura - миксину нужна информация из модулей, поэтому ему нужно что-то знать об этих модулях. Вы сказали, что не можете использовать @import, так как же вы планируете поместить информацию о модуле в файл примеси, чтобы примесь могла ее использовать? Я знаю только @import. Ваше keyframes объяснение все еще трудно понять. Пожалуйста, отредактируйте свой вопрос выше, показав, какая информация поступает в миксин из модуля (и как она туда попадает), что миксин намеревается с ней делать и какой вывод css вы пытаетесь получить. Вы можете расширить свой keyframes пример, просто будьте более явными. - person ScottS; 20.07.2012
comment
@ScottS, я обновил свой вопрос, надеюсь, это поможет вам понять проблему. Спасибо! - person Alex M; 20.07.2012
comment
@JoAsakura, ура, реальная информация. Я бы рассмотрел использование SCSS/Compass, так как он лучше справляется с такими сложными вещами. compass-style.org - person Petah; 22.07.2012
comment
@Petah, да, я знаю, что SCSS в некоторых случаях обеспечивает большую гибкость, но мой проект на node.js лучше поддерживает LESS. - person Alex M; 23.07.2012