前端跨容器的元素拖拽功能

在这里插入图片描述
之前用过一个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'
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IsQtion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值