纯JS 实现列表轻点删除、长按拖拽及错位排序动画

·  阅读 761
纯JS 实现列表轻点删除、长按拖拽及错位排序动画

屏幕录制2021-07-08上午10.37.40-迅捷PDF转换器.gif

这里简单说下实现轻点、长按列表实现错位排序动画实现思路,这里只用到了JQ,没有使用其他的库。胸有成竹、不屑一顾的大神请直接跳到最后点个赞也好,非常感谢。

如何用JS区分轻点和长按?

用户手势交互可以直接使用mouse事件处理(移动端用touch事件),监听mouse事件在对应dom上的状态。

image.png 对于列表item这里采用的className 为menu,当鼠标在列表的item按下时,这里添加一个定时器,定时器延迟操作可以设置长按事件的等待时间。这里记录一下按下时的系统时间。

image.png

在mouseup事件里再去获取一次系统时间,若两次的时间差小于设置的长按事件界定时长,那么处理轻点事件,若大于该时长,处理长按事件。

这里就能区分用户操作是轻点还是长按了。

如何实现轻点删除后的错位动画

这里初始化的所有列表位置布局是用JS创建dom对象并添加CSS样式实现的,本人CSS水平有限,没有想到这类场景如何用CSS去添加动画。创建完所有的item后,计算获取每个item对应位置信息这里标为“rect”属性;先创建rect对象,包含四个属性:x,y,width,height。

获取rect的目的有两个:

1、是在鼠标移动的过程中去检测当前的even触点位置是否在某个可交换的item上。

2、判断进行错位动画时获取需要改变后的调整的位置。

JS动态创建下的dom对象无法改变在文档流中的位置,但是改变后的位置又可能为后续环节使用,所以除了在dom对象上添加了rect属性外,还添加了index属性。这样在排序的过程中,index属性也相应改变,在最后获取排序结果时根据index操作即可。

上面已经分析了如何执行轻点事件,那么当item在执行轻点事件时候进行移除操作,

执行移除当前点击的item

image.png

因为删除了item后面的所有item的index属性都要修改,所以这里添加了一个方法来时现实index的重排。

image.png

注:参数index是要删除item的索引值。

依次改变要删除item后面的全部item的rect,并实现错位动画

修改rect属性 image.png

添加错位动画

image.png

好了,这里就完成了轻点删除及错位动画。

在遍历所有item前调用了一个排序方法获取以index为排序依据的dom集合!!!

image.png

如何设置拖拽排序及错位动画?

这里用到了mousemove方法。

屏幕录制2021-07-08上午10.37.40-迅捷PDF转换器.gif

1、当拖拽开始后,全局保存下当前的所选的item,获取mouse的even的x值跟y值,控制当前所选的item位置跟随触点变化。

2、检测触点移动的位置是否处在可交换的其他item上,若条件符合,根据当前的被拖拽的item的与可交换的item的index属性进行判断,修改二者之间的其他item位置,重置其rect及index属性值。

目录结构

image.png

源码粘出来想看的可以看看,觉得太长的可以忽略,个人随笔,肯定有不严谨的地方,希望大神留言指正,互相学习。

dragPlus.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			* {
				margin: 0;
				padding: 0;
			}

			.wrap {
				width: 380px;
				height: auto;
				zoom: 1;
				overflow: hidden;
				border: 1px solid #8E8E8E;
				margin-top: 100px;
				margin-left: 400px;
				position: relative;
			}

			.menu {
				background-color: darkred;
				color: #FFFFFF;
				font-size: 16px;
				text-align: center;
				line-height: 30px;
				position: absolute;
				display: inline-block;
				z-index: 10;
			}

			.active {
				border: 1px dashed #8E8E8E;
				position: absolute;
				display: inline-block;
				z-index: -1;
			}

			.onclick {
				transform: scale(1.2);
				position: absolute;
				background-color: darkred;
				line-height: 30px;
				height: 30px;
				width: 70px;
				text-align: center;
				color: #FFFFFF;
			}
		</style>
		<script src="http://libs.baidu.com/jquery/1.9.0/jquery.js" type="text/javascript"></script>
		<script src="./dragPlus.js"></script>
	</head>
	<body>
		<div class="wrap" id="wrap"></div>
	</body>
	<script>
		var drag = new DragPlus('wrap', ["测试1", "测试2", "测试3", "测试4", "测试5", "测试1", "测试2", "测试3", "测试4", "测试5", ], 3);
	</script>
	<script src="./dragTouchEven.js"></script>
</html>

复制代码

dragPlus.js

function DragPlus(vm, items, column) {
	this.contain = $("#" + vm);
	this.items = items;
	this.column = column;
	this.sep = 10;
	this.reload();
	this.duran = 500;
	this.currentOptionElement = null;
}

DragPlus.prototype = {
	//刷新
	reload: function() {
		var width = $(this.contain).width();
		var itemWidth = (width - this.sep * (this.column + 1)) / this.column;
		var itemHeight = itemWidth;
		var row = parseInt(this.items.length / this.column) + ((this.items.length % this.column) ? 1 : 0);
		var maxY = itemHeight * row + this.sep * (row + 1);
		for (var i = 0; i < this.items.length; i++) {
			var text = this.items[i];
			var tempDiv = document.createElement("div");
			$(tempDiv).attr('class', 'menu')
			var x = this.sep + (i % this.column) * (itemWidth + this.sep);
			var y = this.sep + parseInt(i / this.column) * (itemWidth + this.sep);
			$(tempDiv).css("left", x);
			$(tempDiv).css("top", y);
			$(tempDiv).text(text);
			$(tempDiv).width(itemWidth);
			$(tempDiv).height(itemHeight);
			$(this.contain).append(tempDiv);
			var rect = {}
			rect.x = x;
			rect.y = y;
			rect.width = itemWidth;
			rect.height = itemHeight;
			$(tempDiv).attr("rect", JSON.stringify(rect))
		}
		$(this.contain).height(maxY)
		this.resetIndex(null);
	},
	//删除
	deleteItem: function(item) {
		var index = $(item).attr("index");
		var rect = $.parseJSON($(item).attr("rect"));
		this.items.splice(index, 1);
		$(item).remove()
		this.resetIndex(index);
		this.addChangePositionAnimate(index, rect);
	},
	//改变位置动画
	addChangePositionAnimate: function(index, rect) {
		var items = this.resort();
		for (var i = items.length - 1; i >= index; i--) {
			var item = items[i];
			var newRect = (i == index) ? rect : $.parseJSON($(items[i - 1]).attr("rect"));
			$(item).attr("rect", JSON.stringify(newRect));
			this.animate(item, newRect);
		}
		var that = this;
		setTimeout(function() {
			that.resetParentRect();
		}, that.duran);
	},
	//添加动画
	animate: function(item, rect) {
		var that = this;
		$(item).animate({
			left: rect.x + 'px',
			top: rect.y + 'px'
		}, that.duran);
	},
	//重置item索引属性
	resetIndex: function(index) {
		if (index == null) {
			var items = document.getElementsByClassName("menu");
			for (var i = 0; i < items.length; i++) {
				var item = items[i];
				$(item).attr("index", i)
			}
		} else {
			var items = this.resort();
			for (var i = index; i < items.length; i++) {
				var item = items[i];
				var nowIndex = $(item).attr("index");
				$(item).attr("index", nowIndex - 1);
			}

		}
	},
	//重置父视图高度
	resetParentRect: function() {
		if (this.items.length == 0) {
			$(this.contain).height(this.sep);
		} else {
			var width = $(this.contain).width();
			var itemHeight = (width - this.sep * (this.column + 1)) / this.column;
			var row = parseInt(this.items.length / this.column) + ((this.items.length % this.column) ? 1 : 0);
			var maxY = itemHeight * row + this.sep * (row + 1);
			$(this.contain).height(maxY)
		}
	},
	//创建old占位div
	createOldPlaceholderElement: function(rect, index) {
		var tempDiv = document.createElement("div");
		$(tempDiv).attr('class', 'active')
		$(tempDiv).attr('id', 'old')
		var x = rect.x;
		var y = rect.y;
		$(tempDiv).css("left", x);
		$(tempDiv).css("top", y);
		$(tempDiv).width(rect.width);
		$(tempDiv).height(rect.height);
		$(tempDiv).attr("rect", JSON.stringify(rect))
		$(tempDiv).attr("index", index);
		$(this.contain).append(tempDiv);
	},
	//移动区间模块
	moveInterval: function(oldElement, newElement) {

		var oIndex = parseInt($(oldElement).attr("index"));
		var nIndex = parseInt($(newElement).attr("index"));

		//console.log('oIndex=' + oIndex + ',' + 'nIndex=' + nIndex);
		var rect = $.parseJSON($(newElement).attr("rect"));
		var oldRect = $.parseJSON($(oldElement).attr("rect"));
		$(oldElement).css({
			"left": rect.x,
			"top": rect.y
		});

		var items = this.resort();

		if (oIndex < nIndex) {
			for (var i = nIndex; i > oIndex; i--) {
				var item = items[i];
				if (item) {
					var tempRect = $.parseJSON($(items[i - 1]).attr("rect"))
					var newRect = (i == oIndex + 1) ? oldRect : tempRect;
					$(item).attr("rect", JSON.stringify(newRect));
					var tempIndex = $(items[i - 1]).attr("index");
					var index = (i == oIndex + 1) ? oIndex : $(items[i - 1]).attr("index");
					$(item).attr("index", index);
					this.animate(item, newRect);
				}
			}
		} else {
			for (var i = nIndex; i < oIndex; i++) {
				var item = items[i];
				if (item) {
					var tempRect = $.parseJSON($(items[i + 1]).attr("rect"))
					var newRect = (i == oIndex - 1) ? oldRect : tempRect;
					$(item).attr("rect", JSON.stringify(newRect));
					var tempIndex = $(items[i + 1]).attr("index");
					var index = (i == oIndex - 1) ? oIndex : $(items[i + 1]).attr("index");
					$(item).attr("index", index);
					this.animate(item, newRect);
				}
			}
		}
		$(oldElement).attr("rect", JSON.stringify(rect))
		$(oldElement).attr("index", nIndex);
		var that = this;
		$(that.currentOptionElement).attr("rect", JSON.stringify(rect))
		$(that.currentOptionElement).attr("index", nIndex);
		setTimeout(function() {
			that.resetParentRect();
		}, that.duran + 10);
	},
	//重新排序
	resort: function() {
		var itemsOld = document.getElementsByClassName("menu");
		var dic = {};
		for (var i = 0; i < itemsOld.length; i++) {
			var item = itemsOld[i];
			dic[$(item).attr("index")] = item;
		}
		var items = [];

		Object.keys(dic).sort().forEach(function(key) {
			items.push(dic[key]);
		});
		return items;
	}
}

复制代码

dragTouchEven.js

$(function() {
	var action = false; //定义一个全局变量来判断鼠标动作,默认为false
	var mouseTapX = 0;
	var mouseTapY = 0;
	var timer = null;
	var isLongPress = false;
	var currentTime = 0;
	var longpressTime = 500;
	var animateTime = 500;
	var currentSelectItem = null;
	$(document).on("mousedown", ".menu", function(event) { //鼠标按下事件
		var that = this;
		currentSelectItem = that;
		isLongPress = true;
		drag.currentOptionElement = that;
		currentTime = Date.parse(new Date());
		timer = setTimeout(function() {
			if (isLongPress) {
				action = true;
				if ($(".onclick").length < 1) {
					var left = $(that).offset().left - $(".wrap").offset().left; //获取在wrap内的偏移量
					var top = $(that).offset().top - $(".wrap").offset().top;
					mouseTapX = event.pageX - $(that).offset().left;
					mouseTapY = event.pageY - $(that).offset().top;
					var rect = $.parseJSON($(that).attr("rect"));
					var index = $(that).attr("index");
					drag.createOldPlaceholderElement(rect, index);
					//$(that).before("<div class='active' id='old'></div>"); //鼠标按下时在原位置创建一个活动的空div
					$(that).css({
						"left": left,
						"top": top,
					});
				}
			}
		}, longpressTime);

	})
	$(document).on("mousemove", ".wrap", function(event) { //wrap上的鼠标移动事件,menu 移动
		if (action == true) {
			var x = event.pageX - $(".wrap").offset().left; //获取鼠标在wrap的偏移量
			var y = event.pageY - $(".wrap").offset().top;
			$(currentSelectItem).css({
				"left": x - mouseTapX,
				"top": y - mouseTapY
			}); //补充内边距
			judgeInsertLeftOrRight(x, y);
		}
	})

	$(document).on("mouseup", function() { //鼠标弹起时将移动的menu 放入新的接受div里,并移除原div位置上的空div

		var nowTime = Date.parse(new Date());
		isLongPress = (nowTime - currentTime >= longpressTime);
		if (!isLongPress) {
			//单点
			drag.deleteItem(currentSelectItem);
		} else {
			//长按
			var oldElement = $("#old");
			if ($(oldElement).attr("rect")) {
				var rect = $.parseJSON($(oldElement).attr("rect"));
				var oIndex = $(oldElement).attr("index");
				$(currentSelectItem).attr("rect", JSON.stringify(rect))
				$(currentSelectItem).attr("index", oIndex);
				$(currentSelectItem).animate({
					left: rect.x + 'px',
					top: rect.y + 'px',
				}, 200);
				$("#old").remove();
			}
		}
		action = false;
	})
})

function deleteItem(currentSelectItem) {
	if (currentSelectItem) {
		drag.deleteItem(currentSelectItem);
	}
}

//判断当前的鼠标点的位置在其他标签的位置
function judgeInsertLeftOrRight(mx, my) {
	var items = document.getElementsByClassName("menu");
	for (var i = 0; i < items.length; i++) {
		var item = items[i];
		var rect = $.parseJSON($(item).attr("rect"));
		if (mx > rect.x && mx < rect.x + rect.width && my > rect.y && my < rect.y + rect.height) {
			insertPlaceholderElement(item);
			return;
		}
	}
}

//标签前后插入新的占位标签
function insertPlaceholderElement(item) {
	drag.moveInterval($("#old"), item);
}


        


复制代码
分类:
前端
分类:
前端
收藏成功!
已添加到「」, 点击更改