1.背景
- 背景:在B-data的二期人群资产中,有一个交互模式是横向排列的几张卡片,最多为5张。当超过容器的展示区域时,需要展示滑动的icon,可支持左右滑动。如果容器可以全部展示卡片,则不展示滑动icon。如下图所示
1.全部展示时,不展示icon
2.tab区域超过容器时展示icon
-
技术定位:初级
-
目标群体:低级前端开发工程师(本人)
-
技术应用场景:前端实现手动滑动的场景
-
整体思路:本文将通过总结两种实现方案,从而分析各自的优缺点以及应用场景。总的来说,就是讨论实现手动滑动,tansform: translateX() 和 scrollLeft两种方法的优缺点。
2.方案一:translateX
2.1 方案描述
代码逻辑:通过判断容器的宽度和滑动区域的宽度来判断是否支持滑动,通过指定滑动区域的transform属性的translateX的值来实现滑动
下面流程图展示了具体的判断逻辑以及需要声明的常量和变量
1.获取container的宽度(指定值)containerWidth 2.获取scroller的宽度,通过ref(如果指定宽度,会失去自适应的特性)scrollerWidth 3.根据1和2计算常量1——最大可偏移值(非负数,最小为0)maxTranslateWidth = (scrollerWidth - containerWidth) < 0 ? 0 : (scrollerWidth - containerWidth) 4.声明变量1,scroller的横向偏移绝对值,初始值为0。scrollWidth = 0 3.声明变量1:左滑按钮是否显示,isShowLeftBtn = maxTranslateWidth > 0 && scrollWidth > 0 4.声明变量2:右滑按钮是否显示,isShowRightBtn = maxTranslateWidth > 0 && scrollWidth < maxTranslateWidth 5.实现滑动按钮点击事件 6.css添加滑动特效
暂时无法在飞书文档外展示此内容
参数列表:罗列代码中涉及的参数和含义
| 参数 | 描述 | 初始值 |
|---|---|---|
| containerWidth | 容器的宽度 | 指定值 |
| scrollerWidth | 滑动区域的宽度 | 指定值或者自适应值 |
| maxTranslateWidth | 最大可滑动的距离,滑动区域的宽度减去容器宽度scrollerWidth - containerWidth | 0 |
| scrollWidth | 滑动区域横向滑动的绝对值 | 0 |
| isShowLeftBtn | 是否展示左滑按钮 | false |
| isShowRightBtn | 是否展示右滑按钮 | false |
2.2 实现过程
Html
<div class="assets-card-tabs" ref="containerElement">
<div class="scroller" ref="scrollerElement" :style="{ transform: `translateX(-${scrollWidth}px)` }">
// ...
</div>
<div
v-if="isShowLeftBtn"
@click.stop.prevent="onScroll(-240)"
class="scroll-btn scroll-btn-left flex justify-center align-center"
>
<i class="biz-icon-arrow-left"></i>
</div>
<div
v-if="isShowRightBtn"
@click.stop.prevent="onScroll(240)"
class="scroll-btn flex justify-center align-center"
>
<i class="biz-icon-arrow-right"></i>
</div>
</div>
Ts
const containerElement = ref<HTMLElement | null>(null)
const scrollerElement = ref<HTMLElement | null>(null)
const containerWidth = ref<number>(0)
const scrollerWidth = ref<number>(0)
onMounted(() => {
// 获取容器的宽度
containerWidth.value = containerElement.value ? containerElement.value.clientWidth : 0
// 获取滑动区域的宽度
scrollerWidth.value = scrollerElement.value ? scrollerElement.value.clientWidth : 0
})
// 计算最大可滑动的距离
const maxTranslateWidth = computed<number>(() => scrollerWidth.value - containerWidth.value < 0 ? 0 : scrollerWidth.value - containerWidth.value)
// 声明滑动变量
const scrollWidth = ref<number>(0)
// 声明是否展示左滑按钮变量。
const isShowLeftBtn = computed<boolean>(() => maxTranslateWidth.value > 0 && scrollWidth.value > 0)
// 声明是否展示右滑按钮变量
const isShowRightBtn = computed<boolean>(() => maxTranslateWidth.value > 0 && scrollWidth.value < maxTranslateWidth.value)
// 实现滑动方法
function onScroll(width: number) {
width = width > maxTranslateWidth.value ? maxTranslateWidth.value : width
scrollWidth.value = scrollWidth.value + width < 0 ? 0 : scrollWidth.value + width
}
Css
.assets-card-tabs {
display: flex;
width: calc(100vw - 280px);
flex-wrap: nowrap;
overflow-x: hidden;
padding-right: 48px;
//position: relative;
.scroll-btn {
z-index: 999;
position: absolute;
width: 48px;
height: 48px;
right: 0;
top: 50%;
transform: translateY(-24px);
border-radius: 24px;
background: #ffffff;
font-size: 20px;
font-weight: 700;
box-shadow: 0px 0px 5px rgba(51, 51, 51, 0.05),
0px 12px 28px rgba(51, 51, 51, 0.04);
cursor: pointer;
}
.scroll-btn-left {
left: 0;
}
.scroller {
display: flex;
flex-wrap: nowrap;
// 添加滑动特效
transition: all .3s;
}
&::-webkit-scrollbar {
height: 0 !important;
}
}
2.3 性能分析
该方案的核心点在于css方法实现滑动
transform:
translateX(-${scrollWidth}px)该方法不会使页面产生Layout重排,因此对于页面性能而言更好
2.4 总结
优点
- 性能好
- 有过渡动画
缺点
-
代码量太多了
-
不太好理解
3.方案二:scrollLeft
3.1 方案描述
代码逻辑:通过元素的scorllLeft来判断是否能够滑动,通过指定元素的scorllLeft来实现左右滑动
注意:只有可滑动的元素,指定起scrollLeft时才会生效,当scrollLeft不可滑动或者大于最大可滑动值的时候,scrollLeft都为0。因此,判断元素是否可滑动的方法为,获取该元素节点,为其scrollLeft赋值,再判断scrollLeft是否为0。判断完毕后scrollLeft再赋值为0.
暂时无法在飞书文档外展示此内容
参数列表:罗列代码中涉及的参数和含义
| 参数 | 含义 |
|---|---|
| scrollLeft | 元素向左滑动的距离 |
3.2 实现过程
HTML
<div class="assets-card-tabs" ref="containerElement">
<div class="scroller" ref="scrollerElement">
// ...
</div>
<div
v-if="isShowLeftBtn"
@click.stop.prevent="onScroll(-240)"
class="scroll-btn scroll-btn-left flex justify-center align-center"
>
<i class="biz-icon-arrow-left"></i>
</div>
<div
v-if="isShowRightBtn"
@click.stop.prevent="onScroll(240)"
class="scroll-btn flex justify-center align-center"
>
<i class="biz-icon-arrow-right"></i>
</div>
</div>
JS
const assetsTabs = ref<HTMLElement | null>(null)
const canScroll = ref<boolean>(false)
const hasScroll = ref<boolean>(false)
onMounted(() => {
if (assetsTabs.value) {
assetsTabs.value.scrollLeft = 1
if (assetsTabs.value.scrollLeft !== 0) {
canScroll.value = true
} else {
canScroll.value = false
}
assetsTabs.value.scrollLeft = 0
}
})
function onScroll(width: number) {
if (!assetsTabs.value) return
assetsTabs.value.scrollLeft += width
if (assetsTabs.value?.scrollLeft) {
hasScroll.value = true
} else {
hasScroll.value = false
}
}
3.3 性能分析
每次获取scollLeft会导致页面的重排,对页面的流畅使用有影响。
3.4 总结
优点
- 代码少
- 逻辑容易理解
缺点
- 会导致页面重排,性能不好
- 没有过渡动画,页面使用体验不好
4.总结
是选择性能好的,还是代码少的,这是个问题。但是总的来说,第一种方法可以实现左右按钮的展示以及有过渡动画,还是第一种更好。