共计 7792 个字符,预计需要花费 20 分钟才能阅读完成。
目录
版权申明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协定,转载请附上原文出处链接和本申明。
- 效果图
- 需要剖析
-
实现剖析
- 款式展现剖析
- 变量剖析
- 办法剖析
-
实现步骤
-
- 实现模板
-
- 实现 css
-
- 首先获取 list
-
- 页面挂载后监听 groupBoxRef 的 scroll 事件并获取以后的滚动地位
-
- 计算展现的宽度显隐箭头,当卡片宽度大于外层宽度就展现
-
- 管制箭头展现方向
-
- 监听外层宽度扭转和窗口大小扭转箭头显隐
-
- 残缺代码
效果图
把之前实现的一个效果图摘出来记录一下,外围代码在这里,如果我的项目中用到其余的技术,也很容易改。
需要剖析
- 展现数据
始终一行
,多余的局部能够出滚动条,同时暗藏滚动条款式。 - 反对笔记本
触控滑动
展现 - 反对
鼠标点击滑动
,多余的时候呈现箭头按钮,默认滑动 3 个卡片的地位,顶头就切换方向 - 当
页面呈现变动的时候要监听
及时显示或暗藏按钮
实现剖析
款式展现剖析
-
外层管制总体组件宽度
-
内层箭头区域展现,外部应用 flex 布局,相对定位到右侧
- 外部箭头 svg 图标,垂直居中
-
内层管制滚动区域宽度,外部应用 flex 布局,管制一层展现,溢出滚动展现,并暗藏滚动条
- 外部确定卡片宽高和间距,最初一个左边距为 0
变量剖析
-
- 卡片
list:Array
- 管制箭头显隐
arrowShow:Boolean
,管制箭头方向direction:String
- 获取滚动地位
scrollPosition = {left: 0, top: 0}
- 计算宽度须要的 ref:管制滚动条层
groupBoxRef
,卡片groupCardRef
办法剖析
- 获取 list(能够 http,也能够 props,依据需要来定)
- 页面挂载之后,监听 groupBoxRef 的 scroll 事件和窗口变动的 resize 事件
- 箭头的显隐判断办法,扭转箭头方向的办法
- 鼠标点击箭头的办法
实现步骤
1. 实现模板
<template>
<div class="index-group-box">
<!-- 左边滑动箭头 -->
<div class="scrollX">
<img src='../assets/arrow-left-bold.svg'/>
</div>
<!-- 卡⽚ -->
<div class="index-group-boxIn" ref="groupBoxRef">
<div
v-for="item in groupInfo"
:key="item.id"
ref="groupCardRef"
class="group-card"
>
<div class="card-name">
名称
<span>{{item.name}}</span>
</div>
</div>
</div>
</div>
</template>
2. 实现 css
<style scoped>
.index-group-box {
padding-right: 16px;
position: relative;
box-sizing: border-box;
width: 100%;
}
.scrollX {
width: 16px;
position: absolute;
top: 0;
right: 0;
height: 100%;
background-color: #512D6D;
display: flex;
justify-content: center;
align-items: center
}
.scrollX:hover {
cursor: pointer;
background-color: #65447d;
}
.index-group-boxIn {
display: flex;
scroll-behavior: smooth;
white-space: nowrap;
overflow-x: auto;
flex: none;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE 10+ */
}
.index-group-boxIn::-webkit-scrollbar {display: none; /* Chrome Safari */}
.group-card {
padding: 8px 16px;
box-sizing:border-box;
width: 200px;
height: 100px;
border-radius: 4px;
margin-right: 16px;
flex: none;
background: #71EFA3;
color: #54436B;
}
.group-card span{color: #54436B;}
.group-card:hover{background: #ACFFAD;}
.group-card:nth-last-of-type(1){margin-right: 0px;}
</style>
3. 首先获取 list
<script>
import {defineComponent, ref} from 'vue';
export default defineComponent({
name: 'scroll',
setup() {const groupInfo = ref([]);
// 获取卡片列表
const getMyGroup = async () => {
const data = [{
id: 1,
name:'卡片 1'
},{
id: 2,
name:'卡片 2'
},{
id: 3,
name:'卡片 3'
},{
id: 4,
name:'卡片 4'
},{
id: 5,
name:'卡片 5'
}]
groupInfo.value = data;
}
getMyGroup();
return {
// data
groupInfo,
};
},
});
</script>
4. 页面挂载后监听 groupBoxRef 的 scroll 事件并获取以后的滚动地位
// 增加 reactive 和 onMounted
import {defineComponent, ref, reactive, onMounted}
...
const groupBoxRef = ref(null); // 获取外层卡⽚ ref
const groupCardRef = ref(null); // 获取卡⽚ ref
const scrollPosition = reactive({
left: 0,
top: 0
}); // 滚动地位
...
// 获取 scroll 函数的地位
const handleScroll = e => {
scrollPosition.left = e.target.scrollLeft;
scrollPosition.top = e.target.scrollTop;
}
getMyGroup();
onMounted(() => {
// 监听 scroll 事件
groupBoxRef.value.addEventListener('scroll', handleScroll, true);
})
return {
// data
groupInfo,
// ref
groupBoxRef,
groupCardRef,
};
5. 计算展现的宽度显隐箭头,当卡片宽度大于外层宽度就展现
- 卡片宽度:
groupCardRef.value.offsetWidth
- 外层宽度:
groupBoxRef.value.offsetWidth
- 滚动区域宽度:
卡片数量 *(卡片宽度 + 左边距)- 最初一个左边距
<div class="scrollX" v-if="arrowShow">
<img src='../assets/arrow-left-bold.svg'/>
</div>
...
const arrowShow = ref(false); // 滚动箭头是否显示
// 获取卡⽚宽度,第⼀个参数是卡⽚个数,默认是整个数组,第⼆个参数是残余的 margin
const getWidth = (num = groupInfo.value.length, restMargin = 16) => {
// 如果没有内容就返回 0
if(!groupCardRef.value) return 0;
return num * (groupCardRef.value.offsetWidth + 16) - restMargin;
}
// 判断 arrow 是否展现
const checkArrowShow = () => {arrowShow.value = getWidth() > groupBoxRef.value?.offsetWidth ? true : false;
}
...
onMounted(() => {
// 监听 scroll 事件
groupBoxRef.value.addEventListener('scroll', handleScroll, true);
// 首次查看箭头展现
checkArrowShow();})
6. 管制箭头展现方向
- 初始朝右,
横向滚动区域为 0 就朝右,残余宽度比外层宽度小就朝左
- 残余宽度:
滚动区域宽度 - 滚动间隔
<!-- 增加点击箭头事件和箭头方向 svg -->
<div class="scrollX" @click="groupScroll" v-if="arrowShow">
<img v-if="direction ==='left'"src='../assets/arrow-left-bold.svg'/>
<img v-else src='../assets/arrow-right-bold.svg'/>
</div>
...
const direction = ref('right'); // 默认项⽬组箭头向右
...
// 扭转滚动⽅向
const changeArrow = (scrollLeft) => {
// 默认获取 scoll 局部整个宽度
const getScrollWidth = getWidth();
// 计算得出残余宽度
const restWidth = getScrollWidth - scrollLeft
if (restWidth <= groupBoxRef.value.offsetWidth) {direction.value = 'left'} else if (scrollLeft === 0) {direction.value = 'right'}
}
// ⿏标点击滚动
const groupScroll = async () => {
// 计算挪动宽度,当初是挪动 3 个卡片的数量
const getMoveWidth = getWidth(3, 0);
// 如果方向是左边就 +,右边就 -
if (direction.value === 'right') {groupBoxRef.value.scrollLeft += getMoveWidth;} else {groupBoxRef.value.scrollLeft -= getMoveWidth;}
// 滚动须要工夫能力获取最新的间隔,依据新的间隔看箭头的方向
setTimeout(() => {changeArrow(groupBoxRef.value.scrollLeft);
}, 500)
}
// 触摸板滑动的时候地位实时扭转箭头方向
const handleScroll = e => {
...
changeArrow(scrollPosition.left);
}
return {
// 新加的 data
...
direction,
// ref
...
// 新加的 methods
groupScroll
};
7. 监听外层宽度扭转和窗口大小扭转箭头显隐
import {defineComponent, ref, reactive, onMounted, watchEffect} from 'vue';
...
watchEffect(() => {checkArrowShow();
})
onMounted(() => {
...
// 监听窗⼝变动事件,判断 arrow 的展现
window.addEventListener('resize', checkArrowShow, true);
})
残缺代码
<template>
<div class="index-group-box">
<!-- 左边滑动箭头 -->
<div class="scrollX" @click="groupScroll" v-if="arrowShow">
<img v-if="direction ==='left'"src='../assets/arrow-left-bold.svg'/>
<img v-else src='../assets/arrow-right-bold.svg'/>
</div>
<!-- 卡⽚ -->
<div class="index-group-boxIn" ref="groupBoxRef">
<div
v-for="item in groupInfo"
:key="item.id"
ref="groupCardRef"
class="group-card"
>
<div class="card-name">
名称
<span>{{item.name}}</span>
</div>
</div>
</div>
</div>
</template>
<script>
import {defineComponent, ref, reactive, onMounted, watchEffect} from 'vue';
export default defineComponent({
name: 'scroll',
setup() {const groupInfo = ref([]); // 卡片 list
const direction = ref('right'); // 默认箭头向右
const arrowShow = ref(false); // 滚动箭头是否显示
const groupBoxRef = ref(null); // 获取外层卡⽚ ref
const groupCardRef = ref(null); // 获取卡⽚ ref
const scrollPosition = reactive({
left: 0,
top: 0
}); // 滚动地位
// 获取卡片列表
const getMyGroup = async () => {
const data = [{
id: 1,
name:'卡片 1'
},{
id: 2,
name:'卡片 2'
},{
id: 3,
name:'卡片 3'
},{
id: 4,
name:'卡片 4'
},{
id: 5,
name:'卡片 5'
}]
groupInfo.value = data;
}
// 获取卡⽚宽度,第⼀个参数是卡⽚个数,默认是整个数组,第⼆个参数是残余的 margin
const getWidth = (num = groupInfo.value.length, restMargin = 16) => {
// 如果没有内容就返回 0
if(!groupCardRef.value) return 0;
return num * (groupCardRef.value.offsetWidth + 16) - restMargin;
}
// 扭转滚动⽅向
const changeArrow = (scrollLeft) => {
// 默认获取 scoll 局部整个宽度
const getScrollWidth = getWidth();
// 获取残余宽度
const restWidth = getScrollWidth - scrollLeft
if (restWidth <= groupBoxRef.value.offsetWidth) {direction.value = 'left'} else if (scrollLeft === 0) {direction.value = 'right'}
}
// ⿏标点击滚动
const groupScroll = async () => {
// 获取滚动宽度
const getMoveWidth = getWidth(3, 0);
if (direction.value === 'right') {groupBoxRef.value.scrollLeft += getMoveWidth;} else {groupBoxRef.value.scrollLeft -= getMoveWidth;}
// 滚动须要工夫能力获取最新的间隔
setTimeout(() => {changeArrow(groupBoxRef.value.scrollLeft);
}, 500)
}
// 判断 arrow 是否展现
const checkArrowShow = () => {arrowShow.value = getWidth() > groupBoxRef.value?.offsetWidth ? true : false;
}
watchEffect(() => {checkArrowShow();
})
// 获取 scroll 函数的地位
const handleScroll = e => {
scrollPosition.left = e.target.scrollLeft;
scrollPosition.top = e.target.scrollTop;
changeArrow(scrollPosition.left);
}
getMyGroup();
onMounted(() => {
// 监听 scroll 事件
groupBoxRef.value.addEventListener('scroll', handleScroll, true);
// 监听窗⼝变动事件,判断 arrow 的展现
window.addEventListener('resize', checkArrowShow, true);
// 首次查看箭头展现
checkArrowShow();})
return {
// data
groupInfo,
direction,
arrowShow,
// ref
groupBoxRef,
groupCardRef,
// methods
groupScroll
};
},
});
</script>
<style scoped>
.index-group-box {
padding-right: 16px;
position: relative;
box-sizing: border-box;
width: 100%;
}
.scrollX {
width: 16px;
position: absolute;
top: 0;
right: 0;
height: 100%;
background-color: #512D6D;
display: flex;
justify-content: center;
align-items: center
}
.scrollX:hover {
cursor: pointer;
background-color: #65447d;
}
.index-group-boxIn {
display: flex;
scroll-behavior: smooth;
white-space: nowrap;
overflow-x: auto;
flex: none;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE 10+ */
}
.index-group-boxIn::-webkit-scrollbar {display: none; /* Chrome Safari */}
.group-card {
padding: 8px 16px;
box-sizing:border-box;
width: 200px;
height: 100px;
border-radius: 4px;
margin-right: 16px;
flex: none;
background: #71EFA3;
color: #54436B;
}
.group-card span{color: #54436B;}
.group-card:hover{background: #ACFFAD;}
.group-card:nth-last-of-type(1){margin-right: 0px;}
</style>
正文完