前端前端FLIP动画实现拖拽排序
SupperFLIP动画实现拖拽排序
1.概念
- First:元素的初始状态,记录当前元素的位置、尺寸、透明度等等的样式信息
- Last:元素的最终状态,即动画后元素的位置、尺寸、透明度等等的样式信息
- Invert:将元素恢复至动画前状态,即相反操作,先计算出从初始状态到最终状态元素发生的改变,比如宽度、高度、透明度等,然后在元素上应用 transform 或 opacity 使这些改变反转,给人一种错觉,即它原来就在初始位置。
- Play:执行动画,前面的准备工作都做好了,最后就是
Play 了,移除元素上的 transform(将transform置为0或none) 并启用 tansition 相关的动画。
2.拖拽排序
①首先写好样式,此部分不做过多解释
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
| <!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>Drag and Drop with FLIP Animation</title> <style> .list{ width: 250px; margin: auto; } .list-item { width: 200px; height: 60px; background-color: #fff; margin-top: 10px; border:1px solid #1D93AB; border-radius: 10px; text-align: center; line-height: 60px; transition: transform 0.3s ease; }
.moving { background: transparent; color:transparent; border: 0; } </style> </head> <body> <div class="list"> <div draggable="true" class="list-item">1</div> <div draggable="true" class="list-item">2</div> <div draggable="true" class="list-item">3</div> <div draggable="true" class="list-item">4</div> <div draggable="true" class="list-item">5</div> <div draggable="true" class="list-item">6</div> <div draggable="true" class="list-item">7</div> <div draggable="true" class="list-item">8</div> <div draggable="true" class="list-item">9</div> <div draggable="true" class="list-item">10</div> </div> </body> </html>
|
②使用拖拽API实现元素拖拽排序
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
| const list = document.querySelector('.list'); let sourceNode;
list.ondragstart = (e)=>{ setTimeout(()=>{ e.target.classList.add('moving'); }) sourceNode = e.target; }
list.ondragover = (e)=>{ const children = Array.from(list.children);
const sourceIndex = children.indexOf(sourceNode); const targetIndex = children.indexOf(e.target);
if(sourceIndex<targetIndex){ list.insertBefore(sourceNode,e.target.nextElementSibling); }else{ list.insertBefore(sourceNode,e.target); } }
list.ondragend = (e)=>{ e.target.classList.remove('moving') }
|
3.加上FLIP动画
以上代码已经实现了列表元素的拖拽排序,但是没有动画,生硬丑陋,接下来使用FLIP思想,给列表元素加上丝滑动画效果,对ondragenter作以下修改
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 61 62 63 64 65
| list.ondragenter = (e)=>{ e.preventDefault(); if (e.target === list || e.target === sourceNode) { return; } const taregetElement = e.target; let startPosition = taregetElement.getBoundingClientRect();
const children = Array.from(list.children); const sourceIndex = children.indexOf(sourceNode); const targetIndex = children.indexOf(taregetElement);
if(sourceIndex<targetIndex){ list.insertBefore(sourceNode,taregetElement.nextElementSibling); }else{ list.insertBefore(sourceNode,taregetElement); }
const sourceLastIndex = children.indexOf(sourceNode); let lastPosition = taregetElement.getBoundingClientRect(); const disY = startPosition.top - lastPosition.top if(Math.abs(targetIndex-sourceIndex)==1){ animateElement(taregetElement,disY) }else if(targetIndex>sourceIndex){ animateAboveElement(targetIndex,sourceLastIndex,disY) }else{ animateBelowElement(targetIndex,sourceLastIndex,disY) } } function animateElement(element,disY){ element.style.transition = 'none'; element.style.transform = `translateY(${disY}px)`; requestAnimationFrame(() => { element.style.transition = 'transform 0.3s ease'; element.style.transform = 'none'; }); }
function animateAboveElement(targetIndex,sourceLastIndex,disY){ Array.from(list.children).forEach((child,index)=>{ if(index<=targetIndex && index>=sourceLastIndex){ animateElement(child,disY) } }) }
function animateBelowElement(targetIndex,sourceLastIndex,disY){ Array.from(list.children).forEach((child,index)=>{ if(index>=targetIndex && index<=sourceLastIndex){ animateElement(child,disY) } }) }
|
4.完整代码
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
| <!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>Drag and Drop with FLIP Animation</title> <style> .list{ width: 250px; margin: auto; } .list-item { width: 200px; height: 60px; background-color: #fff; margin-top: 10px; border:1px solid #1D93AB; border-radius: 10px; text-align: center; line-height: 60px; transition: transform 0.3s ease; }
.moving { background: transparent; color:transparent; border: 0; } </style> </head> <body> <div class="list"> <div draggable="true" class="list-item">1</div> <div draggable="true" class="list-item">2</div> <div draggable="true" class="list-item">3</div> <div draggable="true" class="list-item">4</div> <div draggable="true" class="list-item">5</div> <div draggable="true" class="list-item">6</div> <div draggable="true" class="list-item">7</div> <div draggable="true" class="list-item">8</div> <div draggable="true" class="list-item">9</div> <div draggable="true" class="list-item">10</div> </div> </body>
<script src="./index.js"></script> </html>
|
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| const list = document.querySelector('.list'); let sourceNode;
list.ondragstart = (e)=>{ setTimeout(()=>{ e.target.classList.add('moving'); },0) sourceNode = e.target; } list.ondragover = (e) => { e.preventDefault(); }; list.ondragenter = (e)=>{ e.preventDefault(); if (e.target === list || e.target === sourceNode) { return; } const taregetElement = e.target; let startPosition = taregetElement.getBoundingClientRect();
const children = Array.from(list.children); const sourceIndex = children.indexOf(sourceNode); const targetIndex = children.indexOf(taregetElement);
if(sourceIndex<targetIndex){ list.insertBefore(sourceNode,taregetElement.nextElementSibling); }else{ list.insertBefore(sourceNode,taregetElement); }
const sourceLastIndex = children.indexOf(sourceNode); let lastPosition = taregetElement.getBoundingClientRect(); const disY = startPosition.top - lastPosition.top if(Math.abs(targetIndex-sourceIndex)==1){ animateElement(taregetElement,disY) }else if(targetIndex>sourceIndex){ animateAboveElement(targetIndex,sourceLastIndex,disY) }else{ animateBelowElement(targetIndex,sourceLastIndex,disY) } }
list.ondragend = (e)=>{ e.target.classList.remove('moving') }
function animateElement(element,disY){ element.style.transition = 'none'; element.style.transform = `translateY(${disY}px)`; requestAnimationFrame(() => { element.style.transition = 'transform 0.3s ease'; element.style.transform = 'none'; }); }
function animateAboveElement(targetIndex,sourceLastIndex,disY){ Array.from(list.children).forEach((child,index)=>{ if(index<=targetIndex && index>=sourceLastIndex){ animateElement(child,disY) } }) }
function animateBelowElement(targetIndex,sourceLastIndex,disY){ Array.from(list.children).forEach((child,index)=>{ if(index>=targetIndex && index<=sourceLastIndex){ animateElement(child,disY) } }) }
|