[toc]
# 1. 背景
有个scss
文件,行数达到了10000多,为什么会这么多呢,因为里面枚举了赛程树的所有状态,即队伍总数与滑动次数的组合。这种方式有如下缺点:
- 开发量大,维护成本高。
- 扩展性不足,比如要把队伍总数增加到512、1024支队伍就很费劲。
- 另外,大量的样式代码,让包体积也会增大,性能也会变差。
这里记录下寻找规律、提取函数的过程。
# 2. 淘汰赛
# 2.1. 提取公共样式
公共样式的提取能用 extend
的话就不要用 mixin
,extend
的话会把多个选择器写在一起,比如:
// 源代码
%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. 双败赛
双败赛比淘汰赛不同的是,写一列有可能与上一列队伍相同,之前采用的方法是枚举,其他完全没必要。
我们只需要把关注点放在每一列上,如果当前列与上一列的队伍数目相同,那么它的translateY
和margin-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-bottom
和translateY
的html
如下:
<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)
可以获取 map
中 key
对应的值。
@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();