src\pages\stats\index.vue
<template>
首先,在大模块的类wrap中包含了view和其他引进组件的小模块,第一个是引入的v-tabs组件
<v-tabs v-model="activeTab" :scroll="false" :value=activeTab :tabs=tabs @change="changeTab"></v-tabs>
-
v-model="activeTab"
:v-model
是Vue.js中的一个特殊指令,用于在表单输入和应用状态之间创建双向数据绑定。在这里,它绑定了一个名为activeTab
的数据属性到<v-tabs>
组件,表示当前激活的选项卡索引或标识符。这意味着当用户切换选项卡时,activeTab
的值会自动更新以反映当前选中的选项卡。 -
:scroll="false"
: 这是一个prop(属性)绑定,使用:
(或v-bind:
的简写)来动态绑定值。这里,它告诉<v-tabs>
组件不要使用滚动条来显示超出容器宽度的选项卡。"false"
表示禁用滚动。 -
:value=activeTab
: 这里的:value
绑定似乎是多余的,因为v-model
已经提供了双向绑定的功能。在标准的Vue和大多数UI库中,<v-tabs>
组件不会直接接受一个value
prop来设置当前活动选项卡的索引,因为v-model
已经承担了这一职责。这可能是对组件用法的一个误解或误用。 -
:tabs=tabs
: 这是另一个prop绑定,它传递了一个名为tabs
的数据属性到<v-tabs>
组件。tabs
属性通常是一个数组,包含了所有选项卡项的配置或数据。每个选项卡项可能包括标签文本、图标、是否禁用等信息。 -
@change="changeTab"
: 这是一个事件监听器,它监听<v-tabs>
组件的change
事件。当选项卡切换时,change
事件会被触发,并执行名为changeTab
的方法。changeTab
方法可以用来处理选项卡切换后的逻辑,比如加载与当前选项卡相关联的数据。
<swiper
:touchable="true"
:current="activeTab"
class="stat-content"
easing-function="easeOutCubic"
:style="{ height: '550px' }"
@change="changeTab2"
>
-
:touchable="true"
: 这是一个prop绑定,表示轮播图是否支持触摸滑动。:
前缀是Vue.js中的v-bind:
指令的简写,用于动态绑定属性值。这里设置为true
,意味着用户可以通过触摸屏幕来滑动轮播图。 -
:current="activeTab"
: 这也是一个prop绑定,用于指定当前显示的轮播项索引或标识符。activeTab
是一个在Vue组件的data函数中定义的数据属性,它的值会决定轮播图当前显示的内容。 -
class="stat-content"
: 这里给<swiper>
组件添加了一个CSS类stat-content
,用于应用样式。这个类应该在组件的样式表或全局样式表中定义。 -
easing-function="easeOutCubic"
: 这个属性看起来是自定义的,用于指定轮播图滑动时的缓动函数。easeOutCubic
是一种缓动效果,表示滑动开始时速度较慢,然后逐渐加快,在结束时再次减慢。不过,请注意,不是所有的Vue UI库都直接支持这种属性的写法,它可能是特定库特有的。 -
:style="{ height: '550px' }"
: 使用:style
绑定来直接应用内联样式。这里将轮播图的高度设置为550px
。 -
@change="changeTab2"
: 这是一个事件监听器,用于监听轮播图内容变化(或滑动)的事件。当事件发生时,会调用组件中的changeTab2
方法。这个方法可以用来处理轮播图内容变化后的逻辑,比如更新某些数据或状态。
<swiper-item class="swiper_item">
<view class="charts-box">
<statMonth></statMonth>
</view>
</swiper-item>
<swiper-item class="swiper_item">
<view class="charts-box">
<statYear></statYear>
</view>
</swiper-item>
<swiper-item class="swiper_item">
<view class="charts-box">
<statRanking></statRanking>
</view>
</swiper-item>
这是三个类似的对于<swiper-item>的调用,只不过内部调用的组件不同。
<script>
首先是对组件引用的声明
import demodata from '@/mockdata/demodata.json';
import statMonth from "@/components/stat/stat-month";
import statYear from "@/components/stat/stat-year";
import statRanking from "@/components/stat/stat-ranking";
对export default部分解读
export default {
components: {
statMonth,
statYear,
statRanking
},
data() {
return {
activeTab: 0,
tabs: ['月数据', '年数据', '排行榜']
}
},
onShow() { },
watch: {
activeTab (newVal) {
// console.log('activeTab newVal:', newVal);
this.activeTab = newVal;
}
},
methods: {
changeTab(index) {
// console.log('当前选中的项:' + index)
this.activeTab = index;
},
changeTab2(e) {
// // console.log(e.detail)
if(e.detail.source === 'touch')
//this.changeTab(e.detail.current);
this.activeTab = e.detail.current;
// console.log('changeTab2:', this.activeTab)
}
}
}
组件定义
- components: 定义了该组件内部使用的子组件,包括
statMonth
、statYear
和statRanking
。这些子组件需要在父组件或当前组件中单独定义或导入。
数据对象
- data(): 返回一个对象,包含组件的响应式数据。这里定义了
activeTab
(用于控制活动标签页的索引)和tabs
(标签页的标题数组)。
观察者
- watch: 定义了一个观察者
activeTab
,用于响应activeTab
数据的变化。然而,这里的实现是多余的,因为当activeTab
被更新时(比如通过changeTab
或changeTab2
方法),它又会被自己更新的值再次触发,这可能导致无限循环或不必要的重复操作。通常,watch
用于执行依赖于数据变化的副作用操作,而不是直接更新数据本身。
方法
- changeTab(index): 接收一个索引值
index
,用于更新activeTab
的值,从而改变活动标签页。 - changeTab2(e): 类似于
changeTab
,但它从事件对象e
中提取当前索引(e.detail.current
),并检查事件来源是否为触摸(e.detail.source === 'touch'
)。如果是,则更新activeTab
的值。这种方法可能用于处理来自特定UI组件(如可滑动标签页)的事件。
<style>
每个代码的<style>部分都是对样式的美化,这里不做过多介绍,详情可以自行去网页上搜素,都有详细解答,下面也是如此
<style>
/* #ifndef APP-NVUE */
page {
display: flex;
flex-direction: column;
box-sizing: border-box;
background-color: #fff;
min-height: 100%;
height: auto;
}
view {
font-size: 14px;
line-height: inherit;
}
.content {
display: flex;
flex-direction: column;
flex: 1;
}
.charts-box {
margin: 20rpx;
display: flex;
align-items: center;
padding-top: 12rpx;
width: 95%;
height: 360px;
}
.example-body {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
padding: 0;
font-size: 14px;
background-color: #ffffff;
}
/* #endif */
.section-box{
display: flex;
flex-direction: row;
align-items: center;
padding: 20rpx;
}
.decoration{
width: 4px;
height: 12px;
border-radius: 10px;
background-color: #2979ff;
}
.section-text{
color: #333;
margin-left: 15rpx;
}
/* #ifdef APP-NVUE */
.warp {
background-color: #fff;
}
/* #endif */
.example-body {
flex-direction: column;
padding: 15px;
background-color: #ffffff;
}
.image {
width: 50rpx;
height: 50rpx;
}
.big-number{
font-size: 50rpx;
font-weight: 700;
font-stretch: condensed;
font-style:oblique;
}
.text {
text-align: center;
font-size: 26rpx;
margin-top: 10rpx;
}
.example-body {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
}
.grid-item-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
justify-content: center;
padding: 15px 0;
}
.banner-image {
width: 750rpx;
height: 400rpx;
}
.swiper-box {
height: 400rpx;
}
.search-icons {
padding: 16rpx;
}
.search-container-bar {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
position: fixed;
left: 0;
right: 0;
z-index: 10;
background-color: #fff;
}
/* #ifndef APP-NVUE || VUE3*/
::v-deep
/* #endif */
.uni-searchbar__box {
border-width: 0;
}
/* #ifndef APP-NVUE || VUE3 */
::v-deep
/* #endif */
.uni-input-placeholder {
font-size: 28rpx;
}
</style>
src\components\v-tabs\v-tabs.vue
<template>
<scroll-view
id="scrollContainer"
:scroll-x="scroll"
:scroll-left="scroll ? scrollLeft : 0"
:scroll-with-animation="scroll"
:style="{ position: fixed ? 'fixed' : 'relative', zIndex: 1993 }"
>
:scroll-x="scroll"
:- 使用Vue的
v-bind
(简写为:
)指令绑定scroll-x
属性到组件的scroll
数据属性上。scroll-x
属性可能用于控制滚动视图是否允许横向滚动。如果scroll
是一个布尔值,当它为true
时,滚动视图允许横向滚动;当它为false
时,则不允许。
- 使用Vue的
:scroll-left="scroll ? scrollLeft : 0"
:- 同样使用
v-bind
指令绑定scroll-left
属性。这个属性的值取决于scroll
的值。如果scroll
为true
,则scroll-left
的值为scrollLeft
(可能是另一个数据属性,表示滚动的起始位置);如果scroll
为false
,则scroll-left
的值为0
,即不滚动到任何位置。
- 同样使用
:scroll-with-animation="scroll"
:- 使用
v-bind
指令绑定scroll-with-animation
属性到scroll
数据属性。这个属性可能控制滚动时是否带有动画效果。当scroll
为true
时,滚动将带有动画;为false
时,滚动则无动画。
- 使用
:style="{ position: fixed ? 'fixed' : 'relative', zIndex: 1993 }"
:- 使用
v-bind:style
(简写为:style
)指令动态绑定样式到scroll-view
组件。这里根据fixed
(可能是一个数据属性)的值来决定position
属性的值。如果fixed
为true
,则position
为fixed
,表示滚动视图的位置是固定的;如果fixed
为false
,则position
为relative
,表示滚动视图的位置是相对于其正常位置定位的。同时,无论fixed
的值如何,zIndex
都被设置为1993
,这有助于控制滚动视图在层叠上下文中的堆叠顺序。
- 使用
<view
class="v-tabs__container"
:style="{
display: scroll ? 'inline-flex' : 'flex',
whiteSpace: scroll ? 'nowrap' : 'normal',
background: bgColor,
height,
padding
}"
>
<view
:class="['v-tabs__container-item', { disabled: !!v.disabled }]"
v-for="(v, i) in tabs"
:key="i"
:style="{
color: current == i ? activeColor : color,
fontSize: current == i ? fontSize : fontSize,
fontWeight: bold && current == i ? 'bold' : '',
justifyContent: !scroll ? 'center' : '',
flex: scroll ? '' : 1,
padding: paddingItem
}"
@click="change(i)"
>
{{ field ? v[field] : v }}
</view>
<view
v-if="!pills"
:class="['v-tabs__container-line', { animation: lineAnimation }]"
:style="{
background: lineColor,
width: lineWidth + 'px',
height: lineHeight,
borderRadius: lineRadius,
left: lineLeft + 'px',
transform: `translateX(-${lineWidth / 2}px)`
}"
></view>
<view
v-else
:class="['v-tabs__container-pills', { animation: lineAnimation }]"
:style="{
background: pillsColor,
borderRadius: pillsBorderRadius,
left: pillsLeft + 'px',
width: currentWidth + 'px',
height
}"
></view>
</view>
外层容器 (<view class="v-tabs__container">
)
- 类名:
v-tabs__container
,用于CSS样式和JavaScript操作中的引用。 - 样式绑定:
:style
属性动态绑定了一个对象,这个对象根据scroll
变量的值来决定显示方式(inline-flex
或flex
)、空白处理方式(nowrap
或normal
)、背景色(bgColor
)、高度(height
)、内边距(padding
)。
标签项 (<view :class="...">
循环部分)
- 类名:每个标签项都绑定了
'v-tabs__container-item'
类,并根据v.disabled
的值决定是否添加'disabled'
类。 - 循环:使用
v-for="(v, i) in tabs"
遍历tabs
数组,为每个标签项生成一个视图。 - 样式绑定:根据当前激活的标签(
current == i
)和其他条件(如bold
)动态设置颜色、字体大小、字体加粗、内容对齐方式、弹性布局(flex
)和内边距。 - 点击事件:点击标签项时会触发
change(i)
方法,通常用于更新current
的值以表示当前激活的标签。 - 内容显示:使用
{{ field ? v[field] : v }}
来决定显示标签项的内容。如果field
存在,则显示v[field]
的值;否则直接显示v
。
标签线 (<view v-if="!pills">
)
- 条件渲染:仅当
pills
为false
时渲染。 - 类名:根据是否应用动画(
lineAnimation
)来添加'animation'
类。 - 样式绑定:定义了背景色、宽度、高度、边框半径、左偏移量和变换(用于居中标签线)。
另一标签 (<view v-else>
)
- 条件渲染:当
pills
为true
时渲染。 - 类名:与标签线类似,但使用的是
'v-tabs__container-pills'
类,并根据是否应用动画来添加'animation'
类。 - 样式绑定:定义了背景色、边框半径、左偏移量、宽度和高度。宽度
currentWidth
可能是根据当前激活标签项的宽度动态计算的。
<view
class="v-tabs__placeholder"
:style="{
height: fixed ? height : '0',
padding
}"
></view>
-
height: fixed ? height : '0'
: 这是一个条件表达式,它根据fixed
这个数据属性的值来决定height
属性的值。如果fixed
为true
,则height
属性的值将设置为组件的height
数据属性的值(这里假设height
是已经定义好的数据属性,其值可以是具体的像素值或百分比等)。如果fixed
为false
,则height
属性的值将被设置为字符串'0'
,即占位符的高度将为0,使得它在视觉上不可见或仅占据最小的空间。
<script>
@property {Number} value 选中的下标
* @property {Array} tabs tabs 列表
* @property {String} bgColor = '#fff' 背景颜色
* @property {String} color = '#333' 默认颜色
* @property {String} activeColor = '#2979ff' 选中文字颜色
* @property {String} fontSize = '28rpx' 默认文字大小
* @property {String} activeFontSize = '28rpx' 选中文字大小
* @property {Boolean} bold = [true | false] 选中文字是否加粗
* @property {Boolean} scroll = [true | false] 是否滚动
* @property {String} height = '60rpx' tab 的高度
* @property {String} lineHeight = '10rpx' 下划线的高度
* @property {String} lineColor = '#2979ff' 下划线的颜色
* @property {Number} lineScale = 0.5 下划线的宽度缩放比例
* @property {String} lineRadius = '10rpx' 下划线圆角
* @property {Boolean} pills = [true | false] 是否胶囊样式
* @property {String} pillsColor = '#2979ff' 胶囊背景色
* @property {String} pillsBorderRadius = '10rpx' 胶囊圆角大小
* @property {String} field 如果是对象,显示的键名
* @property {Boolean} fixed = [true | false] 是否固定
* @property {String} paddingItem = '0 22rpx' 选项的边距
* @property {Boolean} lineAnimation = [true | false] 下划线是否有动画
* @event {Function(current)} change 改变标签触发
描述了一个标签页(Tabs)组件的属性和事件,该组件允许用户在不同的标签之间切换,并且提供了丰富的自定义选项来适应不同的设计需求。尤其注意:
- change:
{Function(current)}
- 改变标签触发的事件。当用户切换标签时,会触发这个事件,并传递当前激活的标签的索引(current
)作为参数。这个事件允许开发者在标签切换时执行自定义的逻辑,如加载对应标签的内容。
export default {
props: {
value: {
type: Number,
default: 0
},
tabs: {
type: Array,
default () {
return []
}
},
bgColor: {
type: String,
default: '#fff'
},
padding: {
type: String,
default: '0'
},
color: {
type: String,
default: '#333'
},
activeColor: {
type: String,
default: '#7d787a'
},
fontSize: {
type: String,
default: '28rpx'
},
activeFontSize: {
type: String,
default: '32rpx'
},
bold: {
type: Boolean,
default: true
},
scroll: {
type: Boolean,
default: true
},
height: {
type: String,
default: '70rpx'
},
lineColor: {
type: String,
default: '#7d787a'
},
lineHeight: {
type: String,
default: '10rpx'
},
lineScale: {
type: Number,
default: 0.5
},
lineRadius: {
type: String,
default: '10rpx'
},
pills: {
type: Boolean,
default: false
},
pillsColor: {
type: String,
default: '#7d787a'
},
pillsBorderRadius: {
type: String,
default: '10rpx'
},
field: {
type: String,
default: ''
},
fixed: {
type: Boolean,
default: false
},
paddingItem: {
type: String,
default: '0 22rpx'
},
lineAnimation: {
type: Boolean,
default: true
}
},
data () {
return {
elId: '',
lineWidth: 30,
currentWidth: 0, // 当前选项的宽度
lineLeft: 0, // 滑块距离左侧的位置
pillsLeft: 0, // 胶囊距离左侧的位置
scrollLeft: 0, // 距离左边的位置
containerWidth: 0, // 容器的宽度
current: 0 // 当前选中项
}
},
这部分代码是对props属性和data数据的一些初始化加定义,方便后续若对页面前端进行动态展示的时候,与用户端进行更好的交互,便于数据的处理。
watch: {
value (newVal) {
// console.log('newVal:', newVal);
this.current = newVal
this.$nextTick(() => {
this.getTabItemWidth()
})
},
// current (newVal) {
// this.$emit('input', newVal)
// },
current (newVal) {
// console.log('current newVal:', newVal);
this.$nextTick(() => {
this.getTabItemWidth()
})
this.$emit('input', newVal)
// this.$emit('update:modelVal', newVal)
},
tabs (newVal) {
this.$nextTick(() => {
this.getTabItemWidth()
})
}
},
value (newVal) { ... }
这个观察器用于观察value
prop的变化。当父组件改变传递给这个子组件的value
值时,这个观察器会被触发。
newVal
参数表示value
prop的新值。- 在观察器内部,首先将组件的
current
数据属性设置为新的value
值,以保持内部状态与外部输入的同步。 - 然后,使用
this.$nextTick
来确保DOM更新完成后再执行getTabItemWidth
方法。这是因为getTabItemWidth
方法可能需要基于更新后的DOM来计算某些尺寸,而Vue的DOM更新是异步的。
current (newVal) { ... }
(第二个定义)
这个观察器也用于观察current
数据属性的变化。但是,由于value
观察器已经负责将value
prop的值同步到current
,这里通常不需要再单独观察current
的变化(除非你有其他特定的逻辑需求)。
- 当
current
变化时,同样使用this.$nextTick
来确保DOM更新完成后再执行getTabItemWidth
方法。 - 使用
this.$emit('input', newVal)
来通知父组件current
的值已经改变。这是Vue自定义事件的一个常见用法,用于实现父子组件之间的双向绑定(尽管在Vue 2中,对于.sync
修饰符的prop,推荐使用update:propName
事件名,但在Vue 3中,.sync
修饰符的工作方式已经改变,且更推荐使用v-model
的变体来实现类似的功能)
注意:如果你使用的是Vue 2,并且value
prop是通过.sync
修饰符传递给子组件的,那么你可能想要使用this.$emit('update:value', newVal)
来代替this.$emit('input', newVal)
,以保持与.sync
修饰符的约定一致。但是,在Vue 3中,.sync
修饰符的行为已经改变,并且更推荐使用v-model
的自定义版本来实现类似的双向绑定。
tabs (newVal) { ... }
这个观察器用于观察tabs
prop的变化。当父组件改变传递给这个子组件的tabs
数组时,这个观察器会被触发。
newVal
参数表示tabs
prop的新值。- 在观察器内部,使用
this.$nextTick
来确保DOM更新完成后再执行getTabItemWidth
方法。这可能是因为tabs
的变化会影响到DOM中标签项的数量或内容,从而需要重新计算它们的宽度。
methods: {
// 产生随机字符串
randomString (len) {
len = len || 32
let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
let maxPos = $chars.length
let pwd = ''
for (let i = 0; i < len; i++) {
pwd += $chars.charAt(Math.floor(Math.random() * maxPos))
}
return pwd
},
// 切换事件
change (index) {
const isDisabled = !!this.tabs[index].disabled
if (this.current !== index && !isDisabled) {
this.current = index
this.$emit('change', index)
}
},
// 获取左移动位置
getTabItemWidth () {
// console.log('123');
let query = uni
.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this)
// #endif
// 获取容器的宽度
query
.select(`#scrollContainer`)
.boundingClientRect((data) => {
if (!this.containerWidth && data) {
this.containerWidth = data.width
}
})
.exec()
// 获取所有的 tab-item 的宽度
// uni.createSelectorQuery().in(this) .selectAll('.v-tabs__container-item')
query.selectAll('.v-tabs__container-item')
.boundingClientRect((data) => {
if (!data) {
return
}
let lineLeft = 0
let currentWidth = 0
if (data) {
// console.log(data);
for (let i = 0; i < data.length; i++) {
if (i < this.current) {
lineLeft += data[i].width
} else if (i == this.current) {
currentWidth = data[i].width
} else {
break
}
}
}
// 当前滑块的宽度
this.currentWidth = currentWidth
// 缩放后的滑块宽度
this.lineWidth = currentWidth * this.lineScale * 1
// 滑块作移动的位置
this.lineLeft = lineLeft + currentWidth / 2
// 胶囊距离左侧的位置
this.pillsLeft = lineLeft
// 计算滚动的距离左侧的位置
if (this.scroll) {
this.scrollLeft = this.lineLeft - this.containerWidth / 2
}
})
.exec()
}
}
randomString(len)
- 目的:生成一个指定长度的随机字符串。
- 参数:
len
(可选),字符串的长度,默认为32。 - 实现:通过定义一个包含可用字符的字符串
$chars
(去除了容易混淆的字符),然后在一个循环中随机选择这些字符来构建最终的字符串。
change(index)
- 目的:切换当前激活的标签页。
- 参数:
index
,要切换到的标签页的索引。 - 实现:首先检查目标标签页是否被禁用(
disabled
属性)。如果没有被禁用且当前索引不等于目标索引,则更新current
为新的索引,并通过$emit
触发一个change
事件,将新的索引作为参数传递给父组件。
getTabItemWidth()
- 目的:计算标签项(tab-item)的宽度,以及滑块(通常用于指示当前激活的标签页)的位置和宽度。
- 实现:
- 使用
uni.createSelectorQuery()
来查询DOM元素。在条件编译中,.in(this)
可能用于确保查询在正确的上下文中执行(尽管在非MP-ALIPAY环境下这个调用是可选的)。 - 查询
#scrollContainer
元素的宽度,并将其存储在containerWidth
中(如果尚未设置)。 - 接着,查询所有
.v-tabs__container-item
元素的宽度。对于每个元素,如果其索引小于当前激活的标签页索引,则累加其宽度到lineLeft
(滑块应该开始的位置)。当达到当前激活的标签页时,记录其宽度为currentWidth
。 - 根据
currentWidth
和缩放比例lineScale
计算缩放后的滑块宽度,并设置滑块的位置(lineLeft
加上currentWidth
的一半)。 - 如果存在滚动(
this.scroll
为真),则计算滚动的距离(使滑块保持在容器中心)。
- 使用
mounted () {
this.elId = 'xfjpeter_' + this.randomString()
this.current = this.value
this.$nextTick(() => {
this.getTabItemWidth()
})
}
}
生命周期钩子(mounted
)
mounted()
:当组件被挂载到DOM上后执行。- 实现:
- 生成一个唯一的
elId
,可能用于某些特定的DOM操作或样式绑定(尽管在这段代码中未直接使用)。 - 将
current
初始化为value
prop的值,确保组件初始化时显示正确的标签页。 - 使用
$nextTick
确保DOM更新完成后执行getTabItemWidth()
,以获取并设置标签项和滑块的相关尺寸和位置。
- 生成一个唯一的
<style>
<style lang="scss" scoped>
.v-tabs {
width: 100%;
box-sizing: border-box;
overflow: hidden;
::-webkit-scrollbar {
display: none;
}
&__container {
min-width: 100%;
position: relative;
display: inline-flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
&-item {
display: flex;
align-items: center;
height: 100%;
position: relative;
z-index: 10;
// padding: 0 11px;
transition: all 0.3s;
white-space: nowrap;
&.disabled {
opacity: 0.5;
color: #999;
}
}
&-line {
position: absolute;
bottom: 0;
}
&-pills {
position: absolute;
z-index: 9;
}
&-line,
&-pills {
&.animation {
transition: all 0.3s linear;
}
}
}
}
</style>