效果
功能点
大概有如下两个功能点:
- 列表横向排列
- 点击左右两个按钮可以左右移动每个item(附加动画)
思路
列表横向排列可以使用flex布局解决,主要逻辑在左右滑动的控制上面。
在样式上来看,左右滑动其实可以看成:
改变position为relative时属性left值。
具体分析如下:
如上图,蓝色为外层div,红色为内层div。
内层div装有列表的各项item,所以它的宽度可能会大于外层div的宽度。外层div采用定宽,同时设置overflow: hidden,使内层div突出的部分隐藏起来。然后通过设置内层div的position: relative,调整left属性的值来达到左右滑动的效果。
如上图,把外层div的宽度称为boxLength,内层div的宽度称为allLength,属性值left的绝对值(因为这里left的值应该为负数)称为leftMove。
基于上面的分析,
如果左右两个按钮要控制滑动,实际上就是控制left的属性值。
先来分析右边的按钮:
假设一个列表项目item的宽度为120px,如果要向右移动3个项目item单位,实际上是将left属性值-360。
而且从上图容易得到,当leftMove+boxLength ≥ allLength的时候,向右滑动就应该暂停。
但这里有个小细节,如果内层div(红)右边隐藏部分的width < 360px,则应该只移动那小于360px的一部分宽度,而不应该直接向左移动360px,不然外层div的右侧就会有一段空白。
基于上面这段逻辑,有:
scrollRight() {
const allLength = this.monitorList.length * 120 // monitorList是项目列表
const boxLength = document.getElementById('list-box').clientWidth // 用clientWidth获取外层div的宽度
if (allLength < boxLength) return // 不需要滑动
const listEl = document.getElementById('list')
const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null).left))
if (leftMove + boxLength + 360 > allLength) {
// 到底的时候
listEl.style.left = '-' + (allLength - boxLength) + 'px'
} else {
listEl.style.left = '-' + (leftMove + 360) + 'px'
}
}
这里有个小tip,获取left属性的时候,不能用
document.getElementById('xxx').style.left
这样得到的值将会是undefined,可以用window.getComputedStyle(listEl, null).left来获取。
Window.getComputedStyle() – Web API 接口参考 | MDN
同理,向左滑动,就是将left的属性值+360px;当leftMove + boxLength – 360 < boxLength的时候,就应该是滚动到最前面了,此时left的属性值就为0。
scrollLeft() {
const allLength = this.monitorList.length * 120
const boxLength = document.getElementById('list-box').clientWidth
if (allLength < boxLength) return
const listEl = document.getElementById('list')
const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
if (leftMove + boxLength - 360 < boxLength) {
// 滚到
listEl.style.left = '0px'
} else {
listEl.style.left = '-' + (leftMove - 360) + 'px'
}
}
最后,要像滑动有动画效果,可以使用transition: left 1s;来实现。
demo
<template>
<div>
<el-card>
<div class="monitor-list">
<!-- 左边按钮 -->
<div class="btn" @click="scrollLeft">
<i class="el-icon el-icon-caret-left" />
</div>
<!-- 中间列表 -->
<div id="list-box" class="list-box">
<div id="list" class="list">
<div v-for="item in monitorList" :key="item.id" class="list-item">
<img v-if="item.status" width="60" height="60" :src="imgList.alive" alt="">
<img v-else width="60" height="60" :src="imgList.down" alt="">
{{ item.name }}
</div>
</div>
</div>
<!-- 右边按钮 -->
<div class="btn" @click="scrollRight">
<i class="el-icon el-icon-caret-right" />
</div>
</div>
</el-card>
</div>
</template>
<script>
export default {
data() {
return {
monitorList: [],
imgList: {
alive: require('@/assets/images/icon/device-alive.png'),
down: require('@/assets/images/icon/device-down.png')
}
}
},
created() {
this.initMonitorList()
},
methods: {
initMonitorList() {
for (let i = 1; i < 21; i++) {
this.monitorList.push({
id: i,
name: `item${i + 1}`,
status: 0
})
}
},
// 左滑动逻辑
scrollLeft() {
const allLength = this.monitorList.length * 120
const boxLength = document.getElementById('list-box').clientWidth
if (allLength < boxLength) return
const listEl = document.getElementById('list')
const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
if (leftMove + boxLength - 360 < boxLength) {
// 到最开始的时候
listEl.style.left = '0px'
} else {
listEl.style.left = '-' + (leftMove - 360) + 'px'
}
},
// 右滑动逻辑
scrollRight() {
const allLength = this.monitorList.length * 120
const boxLength = document.getElementById('list-box').clientWidth
if (allLength < boxLength) return
const listEl = document.getElementById('list')
const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
if (leftMove + boxLength + 360 > allLength) {
listEl.style.left = '-' + (allLength - boxLength) + 'px'
} else {
listEl.style.left = '-' + (leftMove + 360) + 'px'
}
}
}
}
</script>
<style lang="scss" scoped>
.monitor-list {
display: flex;
justify-content: space-between;
height: 95px;
.btn {
border: 1px solid #b3d8ff;
width: 50px;
height: 100px;
line-height: 100px;
text-align: center;
cursor: pointer;
background-color: #ecf5ff;
// icon
font-size: 24px;
color: #409eff;
&:hover {
background-color: #409eff;
color: white;
}
}
.list-box {
width: calc(100vw - 100px);
overflow: hidden;
.list {
width: calc(100vw - 100px);
display: flex;
transform: all 2s;
.list-item {
width: 100px;
height: 95px;
text-align: center;
padding: 10px;
cursor: pointer;
margin-left: 40px;
}
position: relative;
left: 0;
transition: left 1s;
}
}
}
</style>