[toc]

# 1. 背景

有个scss文件,行数达到了10000多,为什么会这么多呢,因为里面枚举了赛程树的所有状态,即队伍总数与滑动次数的组合。这种方式有如下缺点:

  1. 开发量大,维护成本高。
  2. 扩展性不足,比如要把队伍总数增加到512、1024支队伍就很费劲。
  3. 另外,大量的样式代码,让包体积也会增大,性能也会变差。

这里记录下寻找规律、提取函数的过程。

# 2. 淘汰赛

# 2.1. 提取公共样式

公共样式的提取能用 extend 的话就不要用 mixinextend的话会把多个选择器写在一起,比如:

// 源代码
%mt5 {
  margin-top: 5px;
}
.btn {
  @extend %mt5;
}
.block {
  @extend %mt5;
}

// 编译后产物
.btn, .block {
  margin-top: 5px;
}

# 2.2. translateY计算

计算某一列的 translateY,公式为:

function getTranslateY(n) {
  return (2 ** n - 1) * 2.32 / 2
}

当 n 取0, 1, 2, 3, 4, 5, 6时,translateY 分别为:

transform: translateY(0rem);
transform: translateY(1.16rem);
transform: translateY(3.48rem);
transform: translateY(8.12rem);
transform: translateY(17.4rem);
transform: translateY(35.96rem);
transform: translateY(73.08rem);
transform: translateY(147.32rem);
transform: translateY(295.8rem);
transform: translateY(592.76rem);

公式是怎么来的呢,当 n 为 1 时,就有 2 ** n = 2 个赛程, 减去最新一列的 1 个,然后乘以 1 个赛程的高度,然后除以 2,就是上面偏移的距离了,也就是上面的公式了。如果 n 为 2,就有 4 个赛程,n 为 3, 就是 8 个赛程,以此类推。

2.32是这么来的,2.32 = 0.8 + 0.8 + 0.4 + 0.32

下面是代码,$x + rem,可以拼接字符串,不要加双引号。

@mixin sch-item-translate-y($n) {
  $pow: 1;
  $tmp: 0;
  $base: 2;

  @while $tmp < $n {
    $pow: $pow * $base;
    $tmp: $tmp +1;
  }

  $x: calc(($pow - 1) * 2.32 / 2);
  transform: translate3d(0, $x + rem, 0);
}

# 2.3. 冠军队伍的translateY计算

公式如下:

function getChampionTranslateY(n) {
  return (2 ** (n-1) - 1) * 2.32 / 2 + 0.4
}

这个值就是上一列的的translateY加上0.4,就是队伍的一半高度。

枚举下:

transform: translateY(0.4rem);
transform: translateY(1.56rem);
transform: translateY(3.88rem);
transform: translateY(8.52rem);
transform: translateY(17.78rem);
transform: translateY(36.36rem);
transform: translateY(73.48rem);
transform: translateY(147.72rem);
transform: translateY(296.2rem);
transform: translateY(593.16rem);

# 2.4. marginBottom计算

计算某一列的赛程的 marginBottom,公式为:

function getMarginBottom(n) {
  return (2 ** n - 1) * 2.32 + 0.32
}

当 n 取0, 1, 2, 3, 4, 5, 6时,marginBottom 分别为:

margin-bottom: 0.32rem;
margin-bottom: 2.64rem;
margin-bottom: 7.28rem
margin-bottom: 16.56rem;
margin-bottom: 35.12rem;
margin-bottom: 72.24rem;
margin-bottom: 146.48rem;
margin-bottom: 294.96rem;
margin-bottom: 591.92rem;

公式说明,当 n 为 1 时,原来的赛程树是2个,减去最新一列的 1 个,然后乘以 1 个赛程的高度,再加上之前原先的 marginBottom

下面是代码:

@mixin sch-item-margin-bottom($n) {
  $pow: 1;
  $tmp: 0;
  $base: 2;

  @while $tmp < $n {
    $pow: $pow * $base;
    $tmp: $tmp +1;
  }

  $x: calc(($pow - 1) * 2.32 + 0.32);
  margin-bottom: $x +rem;
}

# 3. 双败赛

双败赛比淘汰赛不同的是,写一列有可能与上一列队伍相同,之前采用的方法是枚举,其他完全没必要。

我们只需要把关注点放在每一列上,如果当前列与上一列的队伍数目相同,那么它的translateYmargin-bottom就与上列相同,否则就按上面的公式递增。

所以我们需要提前计算出一个滚动列数组scrollList,代表每一列偏移量。如果是单淘汰赛,scrollList就是:

[
  0 - scrollTime, 
  1 - scrollTime, 
  2 - scrollTime,
  ...
]`

双败赛的scrollList,并不是一直递增的。

computed: {
  /**
  * 赛程树滚动列表
  */
  scrollList() {
    const { battleLenList, scrollTime } = this;
    const res = [0 - scrollTime];

   for (let i = 1;i < battleLenList.length;i++) {
      const last = res[res.length - 1];
      let cur;
      if (battleLenList[i] === battleLenList[i - 1]) {
        // 与上一列队伍相同,则偏移量相同
        cur = last;
      } else {
        cur = last + 1;
      }

      // 如果滑动后第一列小于0,则置为0
      if (scrollTime === i && cur < 0) {
        cur = 0;
      }
      res.push(cur);
    }

    return res;
  }
}

# 4. 代码

# 4.1. 赛程树

关于赛程树margin-bottomtranslateYhtml如下:

 <div
  v-for="(colItem,index) in colList"
  :key="colItem.uniqueKey"
  class="tip-match-pk-schedule-item"
  :class="[
    `tip-match-pk-schedule-item--scroll-${scrollList[index]}`,
  ]"
>
  <!--  -->
</div>

css如下:

@mixin sch-item-margin-bottom($n) {
  $pow: 1;
  $tmp: 0;
  $base: 2;

  @while $tmp < $n {
    $marginBottom: calc(($pow - 1) * 2.32 + 0.32);
    $translateY: calc(($pow - 1) * 2.32 / 2);
    $ChampionTranslateY: calc((($pow / $base) - 1) * 2.32 / 2 + 0.4);

    .tip-match-pk-schedule-item {
      &--scroll-#{$tmp} {
        transform: translateY($translateY + rem);

        .tip-match-pk-schedule-team {
          margin-bottom: $marginBottom + rem;
        }

        &.tip-match-pk-schedule-item__champion {
          transform: translateY($ChampionTranslateY + rem);
        }
      }
    }

    $pow: calc($pow * $base);
    $tmp: $tmp + 1;
  }
}

@include sch-item-margin-bottom(10);

# 4.2. Tab高亮

Tab高亮与赛程树不同,不需要scrollList,它的列增加,index一定增加。

html如下:

<div
  v-for="(teamLenItem, index) in teamLenListWrap"
  :key="teamLenItem.uniqueKey"
  class="tip-match-schedule-tab-item"
  :class="`tip-match-schedule-tab-item--scroll-${index - scrollTime}`"
>
 <!--  -->
</div>

css如下:

@mixin sche-tab-highlight($n) {
  $max: 1;

  @while $n < $max {
    .tip-match-schedule-tab-item {
      &--scroll-#{$n} {

        .tip-match-schedule-tab-icon,
        &::after {
          background: $color-primary;
        }
      }

      &--scroll-#{$n + 1} {
        .tip-match-schedule-tab-icon {
          background: $color-primary;
        }
      }
    }

    $n: $n + 1;
  }
}

@include sche-tab-highlight(-3);

# 5. 效果

首先代码行数大幅减少,由10000行减少到100多行,另外,编译后的产物减少了30k,主要是使用extend继承公共样式的作用、以及规律的提取。


# 6. Tab样式优化(已废弃)

下面定义了一个Map类型的数据,表示某一列的起点和终点。

$tabMap: (
  8: (start: 2,
    end: 8,
  ),
  7: (start: 2,
    end: 14,
  ),
  6: (start: 2,
    end: 12,
  ),
  5: (start: 2,
    end: 10,
  ),
  4: (start: 2,
    end: 8,
  ),
  3: (start: 2,
    end: 6,
  ),
  2: (start: 2,
    end: 4,
  ),
  1: (start: 2,
    end: 2,
  ),
);

遍历这个Map,然后为不同位置的icon指定样式。

:nth-child(-n+3)表示前3个位置,也就是1、2、3。

map-get($map, key)可以获取 mapkey 对应的值。

@mixin sche-tab-style() {

  @each $key,
  $val in $tabMap {
    $start: map-get($val, start);
    $end: map-get($val, end);

    @for $i from $start through $end {
      .tip-match-pk-#{$key}-#{$i} {
        .tip-match-schedule-tab {
          .tip-match-schedule-tab-item {
            &:nth-child(-n+#{$i}) {
              .tip-match-schedule-tab-icon {
                @extend %sche-tab-icon-2;
              }

              &::after {
                background: $color-blue-1;
              }
            }

            &:nth-child(#{$i+1}) {
              .tip-match-schedule-tab-icon {
                @extend %sche-tab-icon;
              }
            }
          }
        }
      }
    }
  }
}

@include sche-tab-style();