2

我在 Less 中有以下代码:

我很确定这可以通过某种 mixin 进一步抽象出来,但我现在已经摸不着头脑了。我希望能够传入一个变量,例如@xs 或@xs-gutter,并让函数填充代码。

有任何想法吗?

.padding
{
    &.bottom {
        padding-bottom: @xs-gutter;
    }
    &.left {
        padding-left: @xs-gutter;
    }
    &.right {
        padding-right: @xs-gutter;
    }
    &.top {
        padding-top: @xs-gutter;
    }

    @media @sm-screen {
        &.bottom {
            padding-bottom: @sm-gutter;
        }
        &.left {
            padding-left: @sm-gutter;
        }
        &.right {
            padding-right: @sm-gutter;
        }
        &.top {
            padding-top: @sm-gutter;
        }
    }
    @media @md-screen {
        &.bottom {
            padding-bottom: @md-gutter;
        }
        &.left {
            padding-left: @md-gutter;
        }
        &.right {
            padding-right: @md-gutter;
        }
        &.top {
            padding-top: @md-gutter;
        }
    }
    @media @lg-screen {
        &.bottom {
            padding-bottom: @lg-gutter;
        }
        &.left {
            padding-left: @lg-gutter;
        }
        &.right {
            padding-right: @lg-gutter;
        }
        &.top {
            padding-top: @lg-gutter;
        }
    }
}
4

2 回答 2

2

您可以使用循环和数组列表来减少代码中的重复。以下是有关如何实现缩减的示例片段。请参阅内联注释以了解代码在做什么。

注意:我已经将实际的填充生成混合作为一个单独的混合,它将边作为参数,因为您可以重新使用该混合来为多个边生成填充(通过将边和排水沟作为参数传递)而不生成媒体查询为他们。

@gutters: 4px, 6px, 8px, 10px; // the gutter sizes corresponding to each screen size
@media-sizes: xs, sm, lg, md; // possible screen sizes
@media-conditions: ~"(min-width: 100px)", ~"(min-width: 150px)", ~"(min-width: 200px)", ~"(min-width: 250px)"; // media condition for each screen size

.media-generator(){
    .loop-sizes(length(@media-sizes)); // loop through all screen sizes
    .loop-sizes(@screenIndex) when (@screenIndex > 0) {
        & when (extract(@media-sizes, @screenIndex) = xs){ // since we need xs as default
            .padding-per-side(extract(@gutters, 1); left; right; bottom; top);
        }
        & when not (extract(@media-sizes, @screenIndex) = xs){ // when screen size is not xs
            @condition: extract(@media-conditions, @screenIndex); // extract media condition corresponding to screen type
            @media @condition{
                .padding-per-side(extract(@gutters, @screenIndex); left; right; bottom; top); // call the mixin to generate padding for all sides
            }
        }
        .loop-sizes(@screenIndex - 1);
    }
}
.padding-per-side(@gutter; @sides...){
    .loop-sides(length(@sides));
    .loop-sides(@index) when (@index > 0){
        @side: extract(@sides, @index);
        &.@{side}{
            padding-@{side}: @gutter;
        }
        .loop-sides(@index - 1);
    }
}
.padding{
    .media-generator(); // generate padding for all sides and screens like in question
}

#demo{ // extra :)
    .padding-per-side(10px; left;right); // generates 10px padding for left and right
}

下面是上面的增强版本,它允许我们只为某些方面生成带有媒体查询的填充。下面的片段和上面的片段之间的区别在于,在这里您可以单独为特定边生成填充以及它们的媒体查询版本。

@gutters: 4px, 6px, 8px, 10px;
@media-sizes: xs, sm, lg, md;
@media-conditions: ~"(min-width: 100px)", ~"(min-width: 150px)", ~"(min-width: 200px)", ~"(min-width: 250px)";

.media-generator(@sides...){
    & when (length(@sides) = 0){
        .loop-sizes(length(@media-sizes));
        .loop-sizes(@screenIndex) when (@screenIndex > 0) {
            & when (extract(@media-sizes, @screenIndex) = xs){
                .padding-per-side(extract(@gutters, 1); left; right; bottom; top);
            }
            & when not (extract(@media-sizes, @screenIndex) = xs){
                @condition: extract(@media-conditions, @screenIndex);
                @media @condition{
                    .padding-per-side(extract(@gutters, @screenIndex); left; right; bottom; top);
                }
            }
            .loop-sizes(@screenIndex - 1);
        }
    }
    & when not (length(@sides) = 0){
        .loop-sizes(length(@media-sizes));
        .loop-sizes(@screenIndex) when (@screenIndex > 0) {
            & when (extract(@media-sizes, @screenIndex) = xs){
                .padding-per-side(extract(@gutters, 1); @sides);
            }
            & when not (extract(@media-sizes, @screenIndex) = xs){
                @condition: extract(@media-conditions, @screenIndex);
                @media @condition{
                    .padding-per-side(extract(@gutters, @screenIndex); @sides);
                }
            }
            .loop-sizes(@screenIndex - 1);
        }
    }   
}
.padding-per-side(@gutter; @sides...){
    .loop-sides(length(@sides));
    .loop-sides(@index) when (@index > 0){
        @side: extract(@sides, @index);
        &.@{side}{
            padding-@{side}: @gutter;
        }
        .loop-sides(@index - 1);
    }
}
.padding{
    .media-generator(left; right); // specify sides if needed else leave blank
}
于 2015-02-11T11:54:26.483 回答
1

我个人详细阐述了 3 种可能的解决方案,从最简单的一种开始,然后优化到第 3 种。最简单的具有更好的可读性,最难的使用双嵌套LOOP

在这里,我报告了 3 个解决方案之间的“通用”代码(为了更直观,我决定保留分离的可能尺寸定义):

@sm-screen:~"(min-width: 320px)";
@md-screen:~"(min-width: 720px)";
@lg-screen:~"(min-width: 1200px)";

@xs-gutter:20px;
@sm-gutter:30px;
@md-gutter:40px;
@lg-gutter:50px;

@property:padding;
//@property:margin;

请注意,@property可能是“ margin”或“ padding”无所谓,只是切换评论。

您可以在此变量定义中附加以下 3 个提案:


解决方案 1

这是最简单的解决方案。缺点是它为每条规则生成不同的媒体查询,导致代码冗余:

.side(top, right, bottom, left);

.side(@possible-values...) 
{
  .generate-property-loop(1, @possible-values);
}

.generate-property-loop(@var; @possible-values) when (@var <= length(@possible-values)) 
{
  //Let's extract values in @var position from list @possible-values
  @direction: extract(@possible-values, @var);

  .@{property}.@{direction} 
  {
        @{property}-@{direction}: @xs-gutter;

        @media @sm-screen 
        {
           @{property}-@{direction}: @sm-gutter;
        }

        @media @md-screen 
        {
           @{property}-@{direction}: @md-gutter;
        }

        @media @lg-screen 
        {
           @{property}-@{direction}: @lg-gutter;
        }

  }

  .generate-property-loop((@var + 1), @possible-values);
}

解决方案 2

一种可能的解决方案是将 mediaquery 移到 LOOP 之外,但它仍然需要明确的 mediaquery 定义:

.side(top, right, bottom, left);

.side(@possible-values...) 
{
  .generate-property-loop(1, @possible-values, @xs-gutter);

  @media @sm-screen 
  {
    .generate-property-loop(1, @possible-values, @sm-gutter);
  }

  @media @md-screen 
  {
    .generate-property-loop(1, @possible-values, @md-gutter);
  }

  @media @lg-screen 
  {
    .generate-property-loop(1, @possible-values, @lg-gutter);
  }

}

.generate-property-loop(@var, @possible-values, @gutter) when (@var <= length(@possible-values)) 
{
  //Let's extract values in @var position from list @possible-values
  @direction: extract(@possible-values, @var);

  .@{property}
  {
     &.@{direction} 
     {
        @{property}-@{direction}: @gutter;
     } 
  }

  .generate-property-loop((@var + 1), @possible-values, @gutter);
}

解决方案 3

使用双嵌套循环,您可以实现完全的灵活性,只需将“directions”和“scren-size”作为参数传递,但会牺牲一点可读性:

.side(top, right, bottom, left);

.side(@possible-values...) 
{
  .generate-property-loop(1, @possible-values, @xs-gutter);

  .mediaquery-loop(sm,md,lg);
}

.mediaquery-loop(@possible-screens...)
{
  .generate-mediaquery-loop(1, @possible-screens);
}

.generate-property-loop(@var, @possible-values, @gutter) when (@var <= length(@possible-values)) 
{
  @direction: extract(@possible-values, @var);

  .@{property}
  {
     &.@{direction} 
     {
        @{property}-@{direction}: @gutter;
     } 
  }

  .generate-property-loop((@var + 1), @possible-values, @gutter);
}

.generate-mediaquery-loop(@var, @possible-sizes) when (@var <= length(@possible-screens)) 
{
  @sizes: extract(@possible-sizes, @var);

  @screen-size: ~"@{sizes}-screen";
  @gutter-size: ~"@{sizes}-gutter";

  @media @@screen-size
  {
    .generate-property-loop(1, @possible-values, @@gutter-size);
  }

  .generate-mediaquery-loop((@var + 1), @possible-screens);
}
于 2015-02-11T16:19:43.940 回答