目录实现原理概述代码实现完整代码实现可拖拽排序的菜单效果大家想必都很熟悉,本次我们通过一个可拖拽排序的九宫格案例来演示其实现原理。 先看一下完成效果: 实现原理概述 拖拽原理 当鼠
可拖拽排序的菜单效果大家想必都很熟悉,本次我们通过一个可拖拽排序的九宫格案例来演示其实现原理。 先看一下完成效果:
拖拽原理
排序原理
页面布局
9块砖头(li元素)相对于外层盒子(ul元素)做绝对定位
<ul id="box">
<li style="background-color:black;top: 10px; left: 10px">1</li>
<li style="background-color:black;top: 10px; left: 220px">2</li>
<li style="background-color:black;top: 10px; left: 430px">3</li>
<li style="background-color:black;top: 220px; left: 10px">4</li>
<li style="background-color:black;top: 220px; left: 220px">5</li>
<li style="background-color:black;top: 220px; left: 430px">6</li>
<li style="background-color:black;top: 430px; left: 10px">7</li>
<li style="background-color:black;top: 430px; left: 220px">8</li>
<li style="background-color:black;top: 430px; left: 430px">9</li>
</ul>
样式如下
* {
margin: 0;
padding: 0;
}
html,
body {
width: 100%;
height: 100%;
}
ul,
li {
list-style: none;
}
ul {
width: 640px;
height: 640px;
border: 10px solid pink;
border-radius: 10px;
margin: 50px auto;
position: relative;
}
li {
width: 200px;
height: 200px;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 100px;
position: absolute;
}
定义砖头的背景色和9大坑位位置
// 定义9大li的预设背景色
var colorArr = [
"red",
"orange",
"yellow",
"green",
"blue",
"cyan",
"purple",
"pink",
"gray",
];
const positions = [
[10, 10], [220, 10], [430, 10],
[10, 220], [220, 220], [430, 220],
[10, 430], [220, 430], [430, 430],
]
找出砖头并丢入一个数组
var ulBox = document.querySelector("#box")
var lis = document.querySelectorAll("#box>li")
lis = toArray(lis)
这里我使用了一个将nodeList伪数组转化为真数组的轮子:
function toArray(pArr){
var arr = []
for(var i=0;i<pArr.length;i++){
arr.push(pArr[i])
}
return arr
}
给所有砖头内置一个position属性
lis.forEach(
(item, index) => item.setAttribute("position", index)
)
定义正在拖动的砖头
var draggingLi = null;
// 正在拖动的砖头的zindex不断加加,保持在最上层
var maxZindex = 9
在身上按下 谁就是【正在拖动的砖头】
lis.forEach(
function (li, index) {
li.style.backgroundColor = colorArr[index]
li.addEventListener(
"selectstart",
function (e) {
// 阻止掉拖选文本的默认行为
e.preventDefault()
}
)
li.addEventListener(
"mousedown",
function (e) {
draggingLi = this
draggingLi.style.zIndex = maxZindex++
}
)
}
)
在任意位置松开鼠标则停止拖拽
document.addEventListener(
"mouseup",
function (e) {
// 当前砖头自己进入位置躺好
const p = draggingLi.getAttribute("position") * 1
// draggingLi.style.left = positions[p][0] + "px"
// draggingLi.style.top = positions[p][1] + "px"
move(
draggingLi,
{
left:positions[p][0] + "px",
top:positions[p][1] + "px"
},
200
// callback
)
// 正在拖拽的砖头置空
draggingLi = null;
}
)
当前砖头从鼠标事件位置回归其坑位时用到动画效果,以下是动画轮子
const move = (element, targetObj, timeCost = 1000, callback) => {
const frameTimeCost = 40;
// 500.00px 提取单位的正则
const regUnit = /[\d\.]+([a-z]*)/;
// 计算动画总帧数
const totalFrames = Math.round(timeCost / frameTimeCost);
// 动态数一数当前动画到了第几帧
let frameCount = 0;
// const getAttrSpeed = (attr) => (parseFloat(targetObj[attr]) - parseFloat(getComputedStyle(element)[attr]))/totalFrames
// 存储各个属性的初始值和动画速度
const ssObj = {};
for (let attr in targetObj) {
// 拿到元素属性的初始值
const attrStart = parseFloat(getComputedStyle(element)[attr]);
// 动画速度 = (目标值 - 当前值)/帧数
const attrSpeed =
(parseFloat(targetObj[attr]) - attrStart) / totalFrames;
// 将【属性初始值】和【属性帧速度】存在obj中 以后obj[left]同时拿到这两个货
// obj{ left:[0px初始值,50px每帧] }
ssObj[attr] = [attrStart, attrSpeed];
}
const timer = setInterval(
() => {
// element.style.left = parseFloat(getComputedStyle(element).left)+"px"
// element.style.top = parseFloat(getComputedStyle(element).top)+"px"
// element.style.opacity = getComputedStyle(element).opacity
// 帧数+1
frameCount++;
for (let attr in targetObj) {
// console.log(attr, ssObj[attr], totalFrames, frameCount);
// 用正则分离出单位
// console.log(regUnit.exec("500px"));
// console.log(regUnit.exec(0));
const unit = regUnit.exec(targetObj[attr])[1];
// 计算出当前帧应该去到的属性值
const thisFrameValue =
ssObj[attr][0] + frameCount * ssObj[attr][1];
// 将元素的属性掰到当前帧应该去到的目标值
element.style[attr] = thisFrameValue + unit;
}
if (frameCount >= totalFrames) {
// console.log(frameCount, totalFrames);
clearInterval(timer);
// for (let attr in targetObj) {
// element.style[attr] = targetObj[attr];
// console.log(attr, getComputedStyle(element)[attr]);
// }
// 如果有callback就调用callback
// if(callback){
// callback()
// }
callback && callback();
}
},
frameTimeCost
);
setTimeout(() => {
for (let attr in targetObj) {
element.style[attr] = targetObj[attr];
// console.log(attr, getComputedStyle(element)[attr]);
}
}, timeCost + frameTimeCost);
// 返回正在运行的定时器
return timer;
};
移动鼠标时 砖头跟随 所有砖头实时洗牌
ulBox.addEventListener(
"mousemove",
function (e) {
if (draggingLi === null) {
return
}
// 拿到事件相对于ulBox的位置
var offsetX = e.pageX - ulBox.offsetLeft - 100
var offsetY = e.pageY - ulBox.offsetTop - 100
offsetX = offsetX < 10 ? 10 : offsetX
offsetY = offsetY < 10 ? 10 : offsetY
offsetX = offsetX > 430 ? 430 : offsetX
offsetY = offsetY > 430 ? 430 : offsetY
// 将该位置设置给draggingLi
draggingLi.style.left = offsetX + "px"
draggingLi.style.top = offsetY + "px"
const newPosition = checkPosition([offsetX, offsetY]);
// 如果当前砖头的position发生变化 则数据重排
const oldPosition = draggingLi.getAttribute("position") * 1
if (newPosition != -1 && newPosition != oldPosition) {
console.log(oldPosition, newPosition);
// 先将当前砖头拽出数组(剩余的砖头位置自动重排)
lis.splice(oldPosition, 1)
// 再将当前砖头插回newPosition
lis.splice(newPosition, 0, draggingLi)
// 打印新数据
// logArr(lis,"innerText")
// 砖头洗牌
shuffle()
}
}
)
坑位检测方法
const checkPosition = (ep) => {
for (let i = 0; i < positions.length; i++) {
const [x, y] = positions[i]//[10,10]
const [ex, ey] = ep//[offsetX,offsetY]
const distance = Math.sqrt(Math.pow(x - ex, 2) + Math.pow(y - ey, 2))
if (distance < 100) {
return i
}
}
// 没有进入任何坑位
return -1
}
砖头洗牌方法
const shuffle = () => {
for (var i = 0; i < lis.length; i++) {
lis[i].style.left = positions[i][0] + "px"
lis[i].style.top = positions[i][1] + "px"
// 更新自己的位置
lis[i].setAttribute("position", i)
}
}
主程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta Http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>九宫格拖拽排序</title>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
width: 100%;
height: 100%;
}
ul,
li {
list-style: none;
}
ul {
width: 640px;
height: 640px;
border: 10px solid pink;
border-radius: 10px;
margin: 50px auto;
position: relative;
}
li {
width: 200px;
height: 200px;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 100px;
position: absolute;
}
</style>
</head>
<body>
<ul id="box">
<li style="background-color:black;top: 10px; left: 10px">1</li>
<li style="background-color:black;top: 10px; left: 220px">2</li>
<li style="background-color:black;top: 10px; left: 430px">3</li>
<li style="background-color:black;top: 220px; left: 10px">4</li>
<li style="background-color:black;top: 220px; left: 220px">5</li>
<li style="background-color:black;top: 220px; left: 430px">6</li>
<li style="background-color:black;top: 430px; left: 10px">7</li>
<li style="background-color:black;top: 430px; left: 220px">8</li>
<li style="background-color:black;top: 430px; left: 430px">9</li>
</ul>
<!--
position 位置
-->
<script src="../../../tools/arr_obj_tool.js"></script>
<script src="../../../tools/animtool.js"></script>
<script>
// 定义9大li的预设背景色
var colorArr = [
"red",
"orange",
"yellow",
"green",
"blue",
"cyan",
"purple",
"pink",
"gray",
];
const positions = [
[10, 10], [220, 10], [430, 10],
[10, 220], [220, 220], [430, 220],
[10, 430], [220, 430], [430, 430],
]
var ulBox = document.querySelector("#box")
var lis = document.querySelectorAll("#box>li")
lis = toArray(lis)
lis.forEach(
(item, index) => item.setAttribute("position", index)
)
var draggingLi = null;
// 正在拖动的砖头的zindex不断加加,保持在最上层
var maxZindex = 9
lis.forEach(
function (li, index) {
li.style.backgroundColor = colorArr[index]
li.addEventListener(
"selectstart",
function (e) {
// 阻止掉拖选文本的默认行为
e.preventDefault()
}
)
li.addEventListener(
"mousedown",
function (e) {
draggingLi = this
draggingLi.style.zIndex = maxZindex++
}
)
}
)
document.addEventListener(
"mouseup",
function (e) {
// 当前砖头自己进入位置躺好
const p = draggingLi.getAttribute("position") * 1
// draggingLi.style.left = positions[p][0] + "px"
// draggingLi.style.top = positions[p][1] + "px"
move(
draggingLi,
{
left: positions[p][0] + "px",
top: positions[p][1] + "px"
},
200
// callback
)
// 正在拖拽的砖头置空
draggingLi = null;
}
)
ulBox.addEventListener(
"mousemove",
function (e) {
if (draggingLi === null) {
return
}
// 拿到事件相对于ulBox的位置
var offsetX = e.pageX - ulBox.offsetLeft - 100
var offsetY = e.pageY - ulBox.offsetTop - 100
offsetX = offsetX < 10 ? 10 : offsetX
offsetY = offsetY < 10 ? 10 : offsetY
offsetX = offsetX > 430 ? 430 : offsetX
offsetY = offsetY > 430 ? 430 : offsetY
// 将该位置设置给draggingLi
draggingLi.style.left = offsetX + "px"
draggingLi.style.top = offsetY + "px"
const newPosition = checkPosition([offsetX, offsetY]);
// 如果当前砖头的position发生变化 则数据重排
const oldPosition = draggingLi.getAttribute("position") * 1
if (newPosition != -1 && newPosition != oldPosition) {
console.log(oldPosition, newPosition);
// 先将当前砖头拽出数组(剩余的砖头位置自动重排)
lis.splice(oldPosition, 1)
// 再将当前砖头插回newPosition
lis.splice(newPosition, 0, draggingLi)
// 打印新数据
// logArr(lis,"innerText")
// 砖头洗牌
shuffle()
}
}
)
const checkPosition = (ep) => {
for (let i = 0; i < positions.length; i++) {
const [x, y] = positions[i]//[10,10]
const [ex, ey] = ep//[offsetX,offsetY]
const distance = Math.sqrt(Math.pow(x - ex, 2) + Math.pow(y - ey, 2))
if (distance < 100) {
return i
}
}
// 没有进入任何坑位
return -1
}
const shuffle = () => {
for (var i = 0; i < lis.length; i++) {
lis[i].style.left = positions[i][0] + "px"
lis[i].style.top = positions[i][1] + "px"
// 更新自己的位置
lis[i].setAttribute("position", i)
}
}
</script>
</body>
</html>
动画轮子
function moveWithTransition(element, targetObj, duration) {
element.style.transition = `all ${duration / 1000 + "s"} linear`;
for (var attr in targetObj) {
element.style[attr] = targetObj[attr];
}
setTimeout(() => {
element.style.transition = "none";
}, duration);
}
const move = (element, targetObj, timeCost = 1000, callback) => {
const frameTimeCost = 40;
// 500.00px 提取单位的正则
const regUnit = /[\d\.]+([a-z]*)/;
// 计算动画总帧数
const totalFrames = Math.round(timeCost / frameTimeCost);
// 动态数一数当前动画到了第几帧
let frameCount = 0;
// const getAttrSpeed = (attr) => (parseFloat(targetObj[attr]) - parseFloat(getComputedStyle(element)[attr]))/totalFrames
// 存储各个属性的初始值和动画速度
const ssObj = {};
for (let attr in targetObj) {
// 拿到元素属性的初始值
const attrStart = parseFloat(getComputedStyle(element)[attr]);
// 动画速度 = (目标值 - 当前值)/帧数
const attrSpeed =
(parseFloat(targetObj[attr]) - attrStart) / totalFrames;
// 将【属性初始值】和【属性帧速度】存在obj中 以后obj[left]同时拿到这两个货
// obj{ left:[0px初始值,50px每帧] }
ssObj[attr] = [attrStart, attrSpeed];
}
const timer = setInterval(
() => {
// element.style.left = parseFloat(getComputedStyle(element).left)+"px"
// element.style.top = parseFloat(getComputedStyle(element).top)+"px"
// element.style.opacity = getComputedStyle(element).opacity
// 帧数+1
frameCount++;
for (let attr in targetObj) {
// console.log(attr, ssObj[attr], totalFrames, frameCount);
// 用正则分离出单位
// console.log(regUnit.exec("500px"));
// console.log(regUnit.exec(0));
const unit = regUnit.exec(targetObj[attr])[1];
// 计算出当前帧应该去到的属性值
const thisFrameValue =
ssObj[attr][0] + frameCount * ssObj[attr][1];
// 将元素的属性掰到当前帧应该去到的目标值
element.style[attr] = thisFrameValue + unit;
}
if (frameCount >= totalFrames) {
// console.log(frameCount, totalFrames);
clearInterval(timer);
// for (let attr in targetObj) {
// element.style[attr] = targetObj[attr];
// console.log(attr, getComputedStyle(element)[attr]);
// }
// 如果有callback就调用callback
// if(callback){
// callback()
// }
callback && callback();
}
},
frameTimeCost
);
setTimeout(() => {
for (let attr in targetObj) {
element.style[attr] = targetObj[attr];
// console.log(attr, getComputedStyle(element)[attr]);
}
}, timeCost + frameTimeCost);
// 返回正在运行的定时器
return timer;
};
伪数组转真数组轮子
function toArray(pArr){
var arr = []
for(var i=0;i<pArr.length;i++){
arr.push(pArr[i])
}
return arr
}
这里大家也可以简单地
const arr = [...pArr]
以上就是javascript实现拖拽排序的方法详解的详细内容,更多关于JavaScript拖拽排序的资料请关注编程网其它相关文章!
--结束END--
本文标题: JavaScript实现拖拽排序的方法详解
本文链接: https://www.lsjlt.com/news/148629.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-01-12
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0