Подтвердить что ты не робот

Использование переменных в именах свойств в LESS (динамические свойства/интерполяция свойств)

Я заметил, что inuit.css, написанный в SASS, содержит .vendor mix-in:

@mixin vendor($property, $value...){
    -webkit-#{$property}:$value;
       -moz-#{$property}:$value;
        -ms-#{$property}:$value;
         -o-#{$property}:$value;
            #{$property}:$value;
}

Есть ли способ воспроизвести это в LESS с некоторыми нечетными функциями, такими как e() и @{}?

4b9b3361

Ответ 1

Обновление: МЕНЬШЕ >= 1.6

Начиная с версии 1.6 (см. changelog) интерполяция имени свойства реализована в LESS. Так что вам больше не нужна магия. (Для более старых версий см. Мой первоначальный ответ.)

Ваш mixin будет работать в основном так:

LESS:

.vendor(@property; @value){
    [email protected]{property}: @value;
       [email protected]{property}: @value;
        [email protected]{property}: @value;
         [email protected]{property}: @value;
            @{property}: @value;
}

/*example*/
.test {
    .vendor(transform, translateX(20px));
}

CSS

.test {
  -webkit-transform: translateX(20px);
  -moz-transform: translateX(20px);
  -ms-transform: translateX(20px);
  -o-transform: translateX(20px);
  transform: translateX(20px);
}

Оригинальный ответ: LESS < 1,6

Насколько мне известно, меньше не добавлена ​​поддержка динамически вставленных свойств, о чем ранее говорилось в SO много раз, см. возможно:

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

Обходной путь №1: вводят динамически генерируемые свойства в значение свойств

Первый вариант обходного пути немного уродлив, но я попробовал его, и он работал над http://less2css.org. Итак, я попытался внедрить динамически созданные свойства в значение другого свойства, которое вы жестко кодируете (которое я просто дал случайному "имени поставщика" -inj здесь и присвоил ему значение ect, но вы можете захотеть используйте что-то полезное вместо этого, если вы уже добавили элемент из всего пакета mixin)

.vendors(@property, @value, @pre: ect) {
    -inj:~"@{pre}; [email protected]{property}: @{value}; [email protected]{property}: @{value}; [email protected]{property}: @{value}; [email protected]{property}: @{value}; @{property}: @{value}";
}

Мы можем попробовать это на примере - может быть, что-то, что может стоить... давайте попробуем трансформировать короткое:

LESS:

.test-class{
    .vendors(transform, matrix(1,0,0,1,20,20));
    .vendors(transform-origin,"10px 10px");
}

вывод CSS:

.test-class {
    -inj: ect; -webkit-transform: matrix(1, 0, 0, 1, 20, 20); -moz-transform: matrix(1, 0, 0, 1, 20, 20); -ms-transform: matrix(1, 0, 0, 1, 20, 20); -o-transform: matrix(1, 0, 0, 1, 20, 20); transform: matrix(1, 0, 0, 1, 20, 20);
    -inj: ect; -webkit-transform-origin: 10px 10px; -moz-transform-origin: 10px 10px; -ms-transform-origin: 10px 10px; -o-transform-origin: 10px 10px; transform-origin: 10px 10px;
}

Кажется, что создается рабочий css, но он чувствует, что kinnda ошибочно =)


Обходной путь №2: вставить динамически генерируемые свойства в имя следующего класса (до версии 1.3.3)

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

1). Определим общий метод mixin (аргумент @rest будет использоваться для выравнивания нескольких блоков поставщика позже)

.vendors(@property, @value, @rest:"") {
    @inject:~"@{rest} [email protected]{property}: @{value}; [email protected]{property}: @{value}; [email protected]{property}: @{value}; [email protected]{property}: @{value}; @{property}: @{value};";
}

2). Создайте виртуальный класс/набор правил, который мы хотим, чтобы поставщики были включены, но мы делаем его как mixin, который в конце создает следующий класс aswel - поэтому мы действительно делаем mixin, который будет рекурсивно строить два или более классов. Например (используя тот же пример, что и выше), мы можем написать что-то вроде этого (обратите внимание на использование @inject во втором вызове .vendor(), чтобы связать два блока поставщика вместе):

.test(@nextclass){
    .vendors(transform, "matrix(2,0,0,2,20,20)");
    .vendors(transform-origin,"10px 10px", @inject);
    (~"{@{inject}} [email protected]{nextclass}"){/*next class properties*/};
}

3) Теперь просто мы переносим этот mixin в другой класс для отображения в css:

.this-class{
    .test(next-class);
}

В результате получится CSS:

.this-class {
  -webkit-transform: matrix(2, 0, 0, 2, 20, 20);
  -moz-transform: matrix(2, 0, 0, 2, 20, 20);
  -ms-transform: matrix(2, 0, 0, 2, 20, 20);
  -o-transform: matrix(2, 0, 0, 2, 20, 20);
  transform: matrix(2, 0, 0, 2, 20, 20);
  -webkit-transform-origin: 10px 10px;
  -moz-transform-origin: 10px 10px;
  -ms-transform-origin: 10px 10px;
  -o-transform-origin: 10px 10px;
  transform-origin: 10px 10px;
}
.next-class {
  /*next class properties*/
}

Выход будет всего лишь в одной строке.

Изменить: Для более удобного форматирования вы можете включить javascript-интерполяцию "\n" и "\t", см. предложение Скотта в комментариях ниже.

Таким образом, вы можете теперь объединить несколько классов, используя метод mixer поставщика, без каких-либо ненужных свойств.


Обходной путь №3: вставить динамически генерируемые свойства в имя следующего класса (v1.4.0) с помощью рекурсии

Я добавляю эту причину. Скотт указал в одном из комментариев, что изменения, которые идут с версией 1.4, не позволят обходному пути №2. Но если мы немного находчивы, мы сможем справиться с этой проблемой. Давайте посмотрим, какие проблемы выше обходного пути и исправить их.

1) Первой проблемой будет то, что интерполяция селектора (~"[email protected]{index}") { ... устарела ", поэтому нам нужно выполнить интерполяцию строк на отдельном шаге. Реализации этого было бы достаточно, чтобы добавить один микс .vendors сверху.

МЕНЬШЕ: (используя предложение Scott newline):

@nl: `"\n\t"`;        
.vendors(@property, @value) {
    @inject:~"@{nl}[email protected]{property}: @{value};@{nl}[email protected]{property}: @{value};@{nl}[email protected]{property}: @{value};@{nl}[email protected]{property}: @{value};@{nl}@{property}: @{value};";
}
.test(@nextclass){
    .vendors(transform, "matrix(2,0,0,2,20,20)");
    @inj: ~"{@{inject}`'\n'`} `'\n'`[email protected]{nextclass}";
    @{inj} {/*next class properties*/}
}
.this-class{
    .test(next-class);
}

вывод CSS:

.this-class {
    -webkit-transform: matrix(2,0,0,2,20,20);
    -moz-transform: matrix(2,0,0,2,20,20);
    -ms-transform: matrix(2,0,0,2,20,20);
    -o-transform: matrix(2,0,0,2,20,20);
    transform: matrix(2,0,0,2,20,20);
} 
.next-class {
    /*next class properties*/
}

2) Вторая проблемабыло бы, что "переменные в mixins больше не" течет "в свою область вызова", но я заметил в версии 1.4.0, что если переменная вводится только в mixin, она все равно может быть вызвана из включенного набора правил, поэтому с небольшая рекурсия, вы можете построить блоки .vendors, а на последнем шаге назначьте их новой переменной, которую вы затем используете для инъекции. Я также получил возбуждение и использовал новую функцию extract(), введенную в этой версии меньше. С переменной @i мы назначаем уровень рекурсии (количество блоков поставщиков, которые нужно вставить).

LESS:

@nl: `"\n\t"`; 
.multi(@props,@vals,1,@inj) {
    @property: extract(@props, 1);
    @value: extract(@vals, 1);
    @inject:~"@{inj}@{nl}[email protected]{property}: @{value};@{nl}[email protected]{property}: @{value};@{nl}[email protected]{property}: @{value};@{nl}[email protected]{property}: @{value};@{nl}@{property}: @{value};";
}

.multi(@props,@vals,@i,@inj:"") when (@i > 0) {
    @property: extract(@props, @i);
    @value: extract(@vals, @i);
    @injnext:~"@{inj}@{nl}[email protected]{property}: @{value};@{nl}[email protected]{property}: @{value};@{nl}[email protected]{property}: @{value};@{nl}[email protected]{property}: @{value};@{nl}@{property}: @{value};";
    .multi(@props,@vals,(@i - 1),@injnext);
}

@properties: "transform-origin" "transform";
@values: "10px 10px" "matrix(2,0,0,2,20,20)";

// string of other properties you want to include in the same class
@p: ~"@{nl}width:20px; @{nl}height:12px; @{nl}background-color:#000;";

.this-class {
    .multi(@properties,@values,2,@p);
    @inj: ~"{@{inject}`'\n'`} `'\n'`.next-class ";
    @{inj} {/**/}
}

вывод CSS:

.this-class {
  width:20px;
  height:12px;
  background-color:#000;
  -webkit-transform: matrix(2, 0, 0, 2, 20, 20);
  -moz-transform: matrix(2, 0, 0, 2, 20, 20);
  -ms-transform: matrix(2, 0, 0, 2, 20, 20);
  -o-transform: matrix(2, 0, 0, 2, 20, 20);
  transform: matrix(2, 0, 0, 2, 20, 20);
  -webkit-transform-origin: 10px 10px;
  -moz-transform-origin: 10px 10px;
  -ms-transform-origin: 10px 10px;
  -o-transform-origin: 10px 10px;
  transform-origin: 10px 10px;
}
.next-class {
  /*next class properties*/
}

Теперь это сработало для меня в версии 1.4.0, но давайте посмотрим, что принесет будущее.

Ответ 2

Я просто хотел добавить, что вы можете использовать "минус" в качестве имени имени, и синтаксический анализатор проигнорирует его, но добавляет остальную часть строки. Таким образом, вы не получите пустой inject:; или не навредите. Он все еще взломан, но эй...:)

.prefix(@property, @value) {
    -:~";[email protected]{property}: @{value}; [email protected]{property}: @{value}; [email protected]{property}: @{value}; [email protected]{property}: @{value}; @{property}: @{value}";
}

Пример:

.prefix(transition, "all .2s, color 0s");

Вывод:

-webkit-transition: all .2s, color 0;
-moz-transition: all .2s, color 0;
-ms-transition: all .2s, color 0;
-o-transition: all .2s, color 0;
transition: all .2s, color 0;