Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize the effect of drag-and-drop sorting by JavaScript

2025-02-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)05/31 Report--

Most people do not understand the knowledge points of this article "JavaScript how to achieve drag-and-drop sorting effect", so the editor summarizes the following content, detailed content, clear steps, and has a certain reference value. I hope you can gain something after reading this article. Let's take a look at this "how to achieve drag-and-drop sorting effect in JavaScript" article.

Let's take a look at the completion effect:

Overview of the principle of implementation

Drag principle

When the mouse is pressed over the [draggable square] (hereinafter referred to as the brick), it starts to listen for mouse movement events.

The brick follows wherever the mouse event is moved.

When the mouse is raised, cancel the monitoring of mouse movement events

Sorting principle

Define the location of 9 pits in advance (left and top relative to the outer box)

Throw 9 bricks into an array so that you can place and change the position of the bricks at will through the splice method.

When dragging a brick, remove it from the array first (the remaining bricks are logically reordered)

At the end of the drag, reinsert the brick back to the target position of the array (rearrange the data at this point)

According to the new serial number, the nine bricks in the array are seated to the nine pits to complete the re-rendering.

Code implementation

Page layout

9 bricks (li element) are absolutely positioned relative to the outer box (ul element)

1 2 3 4 5 6 7 8 9

The style is as follows

* {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;}

Define the background color of the brick and the location of 9 pits

/ / define the preset background colors of 9 li var colorArr = ["red", "orange", "yellow", "green", "blue", "cyan", "purple", "pink", "gray",] / * define 9 pit positions * / const positions = [[10,10], [220,10], [430,10], [10,220], [220,220], [430,220], [10,430], [220,430], [430,430],]

Find the bricks and throw them into an array

Var ulBox = document.querySelector ("# box") var lis = document.querySelectorAll ("# box > li") / * convert lis to a true array * / lis = toArray (lis)

Here I use a wheel that converts a NodeList pseudo array into a true array:

/ * pseudo array to true array pseudo array * / function toArray (pArr) {var arr = [] for (var iArray 0bot I item.setAttribute ("position", index))

Define the brick being dragged

/ * the Li (brick) being dragged * / var draggingLi = null; / / the zindex of the brick being dragged is constantly added, keeping it at the top var maxZindex = 9

Press on the body is [the brick being dragged]

/ * pressing on the body is [the brick being dragged] * / lis.forEach (function (li, index) {li.style.backgroundColor = colorArr [index] / * the text in li is not selectable (disable the default behavior of the selectstart event) * / li.addEventListener ("selectstart") Function (e) {/ / disable the default behavior of dragging selected text e.preventDefault () / * press the mouse on any li = I want to drag it * / li. AddEventListener ("mousedown" Function (e) {draggingLi = this draggingLi.style.zIndex = maxZindex++})})

Release the mouse anywhere and stop dragging

/ * release the mouse anywhere on the page = no longer drag any objects * / document.addEventListener ("mouseup" Function (e) {/ / the current brick enters the position by itself and lies down 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) / / the brick being dragged is empty draggingLi = null })

The current brick uses animation when returning to its pit from the mouse event position. Here are the animated wheels.

/ * Multi-attribute animation * @ param {Element} element elements to be animated * @ param {Object} targetObj attribute target value object encapsulates all the attributes to be animated and their target values * @ param {number} timeCost animation time, unit millisecond * @ param {Function} callback animation end callback function * / const move = (element, targetObj, timeCost = 1000, callback) = > {const frameTimeCost = 40 / / 500.00px regular const regUnit = / [\ d\.] + ([Amurz] *) /; / / calculate the total number of frames of the animation const totalFrames = Math.round (timeCost / frameTimeCost); / / dynamically count the frame number of the current animation let frameCount = 0 / * the speed of querying specific attributes (Tang Pengfei's spicy chicken) * / const getAttrSpeed = (attr) = > (parseFloat (targetObj [attr])-parseFloat (getComputedStyle (element) [attr])) / totalFrames / / stores the initial values and animation speed of each attribute const ssObj = {} / * iterate through all attributes of targetObj * / for (let attr in targetObj) {/ / get the initial value of element attribute const attrStart = parseFloat (getComputedStyle (element) [attr]); / / Animation speed = (target value-current value) / number of frames const attrSpeed = (parseFloat (targetObj [attrStart]) / totalFrames) / / after [attribute initial value] and [attribute frame Speed] are stored in obj, obj [left] gets both goods / / obj {left: [0px initial value, 50px per frame]} ssObj [attr] = [attrStart, attrSpeed] } / * start animation * / 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 / / frames + 1 frameCount++ / * the value of each attribute + = animation speed * / for (let attr in targetObj) {/ / console.log (attr, ssObj [attr], totalFrames, frameCount); / / regular separation of units / / console.log (regUnit.exec ("500px")); / / console.log (regUnit.exec (0)) Const unit = regUnit.exec (targetObj [attr]) [1]; / / calculate the attribute value const thisFrameValue = ssObj [attr] [0] + frameCount * ssObj [attr] [1] that should go to the current frame / / break the attributes of the element to the target value element.style [attr] = thisFrameValue + unit;} / * determine whether the animation should be terminated by animating multiple attributes in the current frame * / if (frameCount > = totalFrames) {/ / console.log (frameCount, totalFrames) ClearInterval (timer); / * mandatory correction (the user can't see V anyway) * / / for (let attr in targetObj) {/ / element.style [attr] = targetObj [attr]; / / console.log (attr, getComputedStyle (element) [attr]) / / if there is a callback, call callback / / if (callback) {/ / callback () / /} callback & & callback ();}, frameTimeCost) / * perform violence correction one frame after the end of the animation * / setTimeout (() = > {/ * forced correction (anyway, the user can't see V) * / for (let attr in targetObj) {element.style [attr] = targetObj [attr]; / / console.log (attr, getComputedStyle (element) [attr]);}}, timeCost + frameTimeCost) / / return the running timer return timer;}

When moving the mouse, bricks follow all bricks and shuffle in real time.

/ * move the mouse draggingLi to follow the mouse within the ul * / ulBox.addEventListener ("mousemove" Function (e) {/ * if draggingLi is empty, do nothing and directly return * / if (draggingLi = null) {return} / / get the location of the event relative to ulBox var offsetX = e.pageX-ulBox.offsetLeft- 100 var offsetY = e.pageY-ulBox.offsetTop-100 / * correct offset of bricks * / offsetX = offsetX

< 10 ? 10 : offsetX offsetY = offsetY < 10 ? 10 : offsetY offsetX = offsetX >

430? 430: offsetX offsetY = offsetY > 430? 430: offsetY / / set this location to draggingLi draggingLi.style.left = offsetX + "px" draggingLi.style.top = offsetY + "px" / * Real-time detection real-time [pit] * / const newPosition = checkPosition ([offsetX, offsetY]) / / if the position of the current brick changes, data rearrangement const oldPosition = draggingLi.getAttribute ("position") * 1 if (newPosition! =-1 & & newPosition! = oldPosition) {console.log (oldPosition, newPosition) / * data rearrangement * / / first drag the current bricks out of the array (the remaining bricks are automatically rearranged) lis.splice (oldPosition, 1) / / then insert the current bricks back into newPosition lis.splice (newPosition, 0 DraggingLi) / / print new data / / logArr (lis, "innerText") / / Brick shuffle shuffle ()})

Pit position detection method

/ * Real-time detection of pit positions: check whether the distance between ep and 9 big pits is less than 100 * / 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 } 砖头洗牌方法 /* 砖头洗牌:lis中的每块砖去到对应的位置 */ 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) } }完整代码实现 主程序 九宫格拖拽排序 * { 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; } 1 2 3 4 5 6 7 8 9 // 定义9大li的预设背景色 var colorArr = [ "red", "orange", "yellow", "green", "blue", "cyan", "purple", "pink", "gray", ]; /* 定义9大坑位 */ 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 ") / * convert lis to a true array * / lis = toArray (lis) / * build a position attribute into each brick * / lis.forEach ((item, index) = > item.setAttribute (" position ", index)) / * Li (brick) * / var draggingLi = null being dragged / / the zindex of the brick being dragged is constantly added. Keep it on the top layer var maxZindex = 9 / * press on the body is [the brick being dragged] * / lis.forEach (function (li) Index) {li.style.backgroundColor = colorArr [index] / * text in li is not selectable (the default behavior of selectstart events is disabled) * / li.addEventListener ("selectstart" Function (e) {/ / disable the default behavior of dragging selected text e.preventDefault () / * press the mouse on any li = I want to drag it * / li. AddEventListener ("mousedown" Function (e) {draggingLi = this draggingLi.style.zIndex = maxZindex++}) / * release the mouse anywhere on the page = no longer drag and drop any objects * / document.addEventListener ("mouseup" Function (e) {/ / the current brick enters the position by itself and lies down 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) / / the brick being dragged is empty draggingLi = null }) / * move the mouse draggingLi to follow the mouse within the ul * / ulBox.addEventListener ("mousemove" Function (e) {/ * if draggingLi is empty, do nothing and directly return * / if (draggingLi = null) {return} / / get the location of the event relative to ulBox var offsetX = e.pageX-ulBox.offsetLeft- 100 var offsetY = e.pageY-ulBox.offsetTop-100 / * correct offset of bricks * / offsetX = offsetX

< 10 ? 10 : offsetX offsetY = offsetY < 10 ? 10 : offsetY offsetX = offsetX >

430? 430: offsetX offsetY = offsetY > 430? 430: offsetY / / set this location to draggingLi draggingLi.style.left = offsetX + "px" draggingLi.style.top = offsetY + "px" / * Real-time detection real-time [pit] * / const newPosition = checkPosition ([offsetX, offsetY]) / / if the position of the current brick changes, data rearrangement const oldPosition = draggingLi.getAttribute ("position") * 1 if (newPosition! =-1 & & newPosition! = oldPosition) {console.log (oldPosition, newPosition) / * data rearrangement * / / first drag the current bricks out of the array (the remaining bricks are automatically rearranged) lis.splice (oldPosition, 1) / / then insert the current bricks back into newPosition lis.splice (newPosition, 0 DraggingLi) / / print new data / / logArr (lis "innerText") / / Brick shuffle shuffle ()}) / * Real-time detection of pit positions: check whether the distance between ep and 9 big pits is less than 100 * / 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 } /* 砖头洗牌:lis中的每块砖去到对应的位置 */ 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) } } 动画轮子 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) } / * Multi-attribute animation * @ param {Element} element elements to be animated * @ param {Object} targetObj attribute target value object encapsulates all the attributes to be animated and their target values * @ param {number} timeCost animation time, unit millisecond * @ param {Function} callback animation end callback function * / const move = (element, targetObj, timeCost = 1000, callback) = > {const frameTimeCost = 40 / / 500.00px regular const regUnit = / [\ d\.] + ([Amurz] *) /; / / calculate the total number of frames of the animation const totalFrames = Math.round (timeCost / frameTimeCost); / / dynamically count the frame number of the current animation let frameCount = 0 / * the speed of querying specific attributes (Tang Pengfei's spicy chicken) * / const getAttrSpeed = (attr) = > (parseFloat (targetObj [attr])-parseFloat (getComputedStyle (element) [attr])) / totalFrames / / stores the initial values and animation speed of each attribute const ssObj = {} / * iterate through all attributes of targetObj * / for (let attr in targetObj) {/ / get the initial value of element attribute const attrStart = parseFloat (getComputedStyle (element) [attr]); / / Animation speed = (target value-current value) / number of frames const attrSpeed = (parseFloat (targetObj [attrStart]) / totalFrames) / / after [attribute initial value] and [attribute frame Speed] are stored in obj, obj [left] gets both goods / / obj {left: [0px initial value, 50px per frame]} ssObj [attr] = [attrStart, attrSpeed] } / * start animation * / 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 / / frames + 1 frameCount++ / * the value of each attribute + = animation speed * / for (let attr in targetObj) {/ / console.log (attr, ssObj [attr], totalFrames, frameCount); / / regular separation of units / / console.log (regUnit.exec ("500px")); / / console.log (regUnit.exec (0)) Const unit = regUnit.exec (targetObj [attr]) [1]; / / calculate the attribute value const thisFrameValue = ssObj [attr] [0] + frameCount * ssObj [attr] [1] that should go to the current frame / / break the attributes of the element to the target value element.style [attr] = thisFrameValue + unit;} / * determine whether the animation should be terminated by animating multiple attributes in the current frame * / if (frameCount > = totalFrames) {/ / console.log (frameCount, totalFrames) ClearInterval (timer); / * mandatory correction (the user can't see V anyway) * / / for (let attr in targetObj) {/ / element.style [attr] = targetObj [attr]; / / console.log (attr, getComputedStyle (element) [attr]) / / if there is a callback, call callback / / if (callback) {/ / callback () / /} callback & & callback ();}, frameTimeCost) / * perform violence correction one frame after the end of the animation * / setTimeout (() = > {/ * forced correction (anyway, the user can't see V) * / for (let attr in targetObj) {element.style [attr] = targetObj [attr]; / / console.log (attr, getComputedStyle (element) [attr]);}}, timeCost + frameTimeCost) / / return the running timer return timer;}

Pseudo array to true array wheel

/ * pseudo array to true array pseudo array * / function toArray (pArr) {var arr = [] for (var iArray 0

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report