之前用过一个vue的插件叫vue-slicksort,专门用来拖动元素的,但是只支持标签内拖拽,跨标签的拖拽暂时还不知道有没有这样的一款插件。自己动手丰衣足食,虽然这个是在vue环境下作的,但逻辑也都是原生js写的,所以vue还是其他什么框架的也都无所谓。
html结构
<template>
<div class="hello">
<div class="box"
@touchstart="changeViewStart"
@touchmove="changeView">
<div class="content"></div>
<div class="dotList">
<!--这个span是那几个小圆点,拖动项都是在js里面,根据数据动态生成的-->
<span v-for="n in list.length" :key="n" :class="index==n-1?'addColor':''" @click="changeIndex(n-1)"></span>
</div>
</div>
</div>
</template>
CSS样式表
<style scoped>
body,
html {
margin: 0;
padding: 0;
}
.box {
width: 100%;
height: 4.5rem;
overflow: hidden;
border: 1px solid #ccc;
box-sizing: border-box;
position: relative;
}
.content {
position: relative;
left: 0;
height: 4rem;
transition: all 0.5s linear;
}
.box >>> .itemBox {
display: flex;
flex-wrap: wrap;
position: relative;
float: left;
height: 4rem;
}
.box >>> .item {
height: 1.8rem;
box-sizing: border-box;
line-height: 1.8rem;
font-size: 1rem;
background-color: skyblue;
transform: translate(0px,0px);
position: relative;
}
.dotList {
position: absolute;
bottom: 0.15rem;
left: 50%;
transform: translateX(-50%);
}
.dotList span {
width: 0.2rem;
height: 0.2rem;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 50%;
margin: 0 0.2rem;
float: left;
}
.dotList .addColor {
background-color: orangered;
}
</style>
初始化数据
data() {
return {
timer: null,
index: 0, //容器的索引
itemBoxIndex: 0,
itemIndex: 0,
startX: 0, //切换banner时手指位置
timeStamp: 0, //切换banner的触屏时间
itemX: 0, //拖拽项的起始点击位置
itemY: 0, //拖拽项的起始点击位置
bool: false,
flag: false,
list: [ //被拖动项
["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
["11", "12", "13", "14", "15", "16", "17", "18", "19", "20"],
["21", "22", "23", "24", "25", "26", "27", "28", "29", "30"],
["31", "32", "33", "34", "35", "36", "37", "38", "39", "40"],
["41", "42", "43", "44", "45", "46", "47", "48", "49", "50"],
["51", "52", "53", "54", "55", "56", "57", "58", "59", "60"],
]
};
},
初始化容器及拖动项
init() {
var box = document.querySelector(".content");
box.style.width =
document.documentElement.clientWidth * this.list.length + "px";
//根据list的长度,创建n个父容器
for (let i = 0; i < this.list.length; i++) {
var childBox = document.createElement("div");
childBox.classList.add("itemBox");
box.appendChild(childBox);
childBox.style.width = document.documentElement.clientWidth + "px";
//创建拖动项,获取屏幕宽度,用于分配每个拖动项的宽度,1行5个,每项就是1/5屏幕宽度
for (let j = 0; j < this.list[i].length; j++) {
var child = document.createElement("div");
child.classList.add("item");
childBox.appendChild(child);
child.style.width =
((document.documentElement.clientWidth - 2) / 5) * 0.8 + "px";
child.style.margin =
"0.1rem " +
((document.documentElement.clientWidth - 2) / 5) * 0.1 +
"px";
child.innerHTML = this.list[i][j];
child.addEventListener('touchstart',this.touchItem.bind(this,child))
child.addEventListener('touchmove',this.moveItem.bind(this,child))
child.addEventListener('touchend',this.downItem.bind(this,child))
}
}
},
切换容器
//点击小圆点,切换对应容器
changeIndex(n) {
this.index = n
},
//手指滑动切换容器
changeViewStart(e){
this.startX = e.touches[0].clientX
this.timeStamp = e.timeStamp
this.bool = true
},
changeView(e){
if(Math.abs(this.startX-e.touches[0].clientX) > 200 && e.timeStamp - this.timeStamp < 400 && this.bool) {
this.bool = false
if(this.startX>e.touches[0].clientX) {
this.index = this.index+1>=this.list.length?this.list.length-1:this.index+1
}else {
this.index = this.index<=0?0:this.index-1
}
}
},
拖动项的逻辑
//开始拖动
touchItem(ele){
var e = window.event.touches[0]
this.itemBoxIndex = this.index
this.itemX = e.clientX
this.itemY = e.clientY
var box = document.querySelectorAll('.itemBox')[this.index]
for(let i=0;i<this.list[this.index].length;i++){
if(ele == box.childNodes[i] ) {
this.itemIndex = i
}
}
},
//拖动过程中
moveItem(ele) {
clearInterval(this.timer)
var con = document.querySelectorAll('.itemBox')
var bs = this.index*document.documentElement.clientWidth
var top = document.querySelector('.box').offsetTop
var e = window.event.touches[0]
var docWidth = document.documentElement.clientWidth
var box = document.querySelector('.box')
if(this.index>this.itemBoxIndex){
ele.style.transform = 'translate('+(e.clientX-this.itemX+(this.index-this.itemBoxIndex)*docWidth)+'px,'+(e.clientY-this.itemY)+'px)'
}else if(this.index<this.itemBoxIndex){
ele.style.transform = 'translate('+(e.clientX-this.itemX-(this.itemBoxIndex-this.index)*docWidth)+'px,'+(e.clientY-this.itemY)+'px)'
}else {
ele.style.transform = 'translate('+(e.clientX-this.itemX)+'px,'+(e.clientY-this.itemY)+'px)'
}
ele.style.zIndex = '999'
for(let i=0;i<this.list[this.index].length;i++){
if(con[this.index].childNodes[i]!=ele
&&e.clientX+bs >= con[this.index].childNodes[i].offsetLeft+bs
&&e.clientX+bs <= con[this.index].childNodes[i].offsetLeft+bs+con[this.index].childNodes[i].clientWidth
&&e.clientY-top >= con[this.index].childNodes[i].offsetTop
&&e.clientY-top <= con[this.index].childNodes[i].offsetTop+con[this.index].childNodes[i].clientHeight) {
con[this.index].childNodes[i].style.opacity='0.5'
}else {
con[this.index].childNodes[i].style.opacity='1'
}
}
this.timer = setInterval(()=>{
if(e.clientX>=docWidth*0.95){
this.index = this.index+1>=this.list.length?this.list.length-1:this.index+1
}else if(e.clientX<=docWidth*0.05){
this.index = this.index<=0?0:this.index-1
}
},1000)
},
//拖动结束
downItem(ele) {
clearInterval(this.timer)
var e = window.event.changedTouches[0]
var top = document.querySelector('.box').offsetTop
var box = document.querySelectorAll('.itemBox')
var bs = this.index*document.documentElement.clientWidth
ele.style.transform = 'translate(0px,0px)'
ele.style.zIndex = '1'
for(let i=0;i<this.list[this.index].length;i++){
if(box[this.index].childNodes[i]!=ele
&&e.clientX+bs >= box[this.index].childNodes[i].offsetLeft+bs
&&e.clientX+bs <= box[this.index].childNodes[i].offsetLeft+bs+box[this.index].childNodes[i].clientWidth
&&e.clientY-top >= box[this.index].childNodes[i].offsetTop
&&e.clientY-top <= box[this.index].childNodes[i].offsetTop+box[this.index].childNodes[i].clientHeight) {
let tempItemOne = this.list[this.itemBoxIndex][this.itemIndex]
let tempItemTwo = this.list[this.index][i]
this.$set(this.list[this.itemBoxIndex], this.itemIndex, tempItemTwo)
this.$set(this.list[this.index], i, tempItemOne)
for(let i=0;i<document.querySelector('.content').childNodes.length;i++){
document.querySelector('.content').removeChild(document.querySelector('.content').childNodes[i])
i--
}
this.init()
}
}
}
初始化和事件监听
mounted() {
this.init()
},
watch: {
index(value){
var box = document.querySelector('.content');
box.style.left = - value*document.documentElement.clientWidth+'px'
}
}