先上效果:

实现思路和流程:

  1. 基础页面布局 给每个拖动元素加上 draggable="true"
  2. ondragstart(开始拖动某个元素时)做出 对应的处理 获得操作的具体元素 给目标元素添加对应的样式 显示透明 增加虚线描边
  3. ondragover 被拖动的元素hover到目标元素上时触发 阻止默认事件-默认不让元素拖动到自身
  4. ondragenter 拖动进行当中 对比 当前拖动的元素和 正在覆盖元素的索引 来判断操作 是上升还是下降
  5. ondragend 在结束时 样式由内部透明 虚线转为 原来的样子
  6. 配置flip动画

基础页面布局:

<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><style>.list{width: 750px;margin: 40px auto;}.list_item{width: 100%;border-radius: 8px;height: 52px;margin-bottom: 12px;background: rgba(40,142,145,0.9);color: white;line-height: 52px;padding-left: 16px;font-size: 18px;box-sizing: border-box;cursor: move;/*user-select: none;*/}.moving{background: transparent;color: transparent;border: 1px  dashed #ccc;}
</style>

实现拖动步骤

获取到总的外容器 便于下面事件委托
const list = document.querySelector('.list')
获取到所有可拖动的元素 用于记录起始位置
const item = document.querySelectorAll('.list_item')
let  sourceNode;  判断当前拖动的是哪个元素开始拖动的事件
list.ondragstart = e =>{sourceNode = e.targetrecord(item)  传入item 记录起始位置setTimeout(()=>{e.target.classList.add('moving')},0)e.dataTransfer.effectAllowed = 'move'
}
list.ondragover = e => {e.preventDefault()
}拖动进行中的事件
list.ondragenter = e =>{e.preventDefault()托回到原来的位置了就什么也不做if(e.target === list || e.target === sourceNode){return false}const children = Array.from(list.children)const sourceIndex = children.indexOf(sourceNode)  当前劫持元素的索引值const targetIndex = children.indexOf(e.target)   覆盖到谁上面的索引值if(sourceIndex < targetIndex){父节点.insertBefore(要插入的节点,在谁前面) 从下向上拖动list.insertBefore(sourceNode,e.target.nextElementSibling)}else {list.insertBefore(sourceNode,e.target)}
  last([e.target,sourceNode]) 传入改变位置的两个元素 比较差异 执行filp动画
}拖动结束的时候取消虚线
list.ondragend = e =>{e.target.classList.remove('moving')
}

filp动画的函数

// 记录初始位置
function record(eleAll) {for( let i = 0;i < eleAll.length; i++ ) {const { top,left } = eleAll[i].getBoundingClientRect()eleAll[i]._top_ = topeleAll[i]._left_ = left}
}/*  getBoundingClientRect()用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗(可视范围不包含卷去的部分)的位置。*/
/*** requestAnimationFrame 比起 setTimeout、setInterval的优势主要有两点:1、requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。2、在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。取消:cancelAnimationFrame(Id)* **/​​​​​​​// 记录最后的位置 并且执行动画
function last(eleAll) {for( let i = 0;i < eleAll.length; i++ ) {const dom = eleAll[i]const { top,left } = dom.getBoundingClientRect()// 新增dom时,逻辑应为 原有dom后移动,新增dom不动,故记录了位置的才添加动画 确定上一步有记录起始位置再进行下一步if(dom._left_) {// 恢复至开始位置dom.style.transform = `translate3d(${ dom._left_ - left }px, ${ dom._top_ - top }px,0px)`// play 过程,移除开始位置的设置,添加过渡let rafId = requestAnimationFrame(function() {//启用tansition,并移除翻转的改变  可以内置样式也可以用 外部类//dom.classList.add('active')dom.style.transition = 'transform 300ms ease-out'dom.style.transform = 'none'})dom.addEventListener('transitionend', () => {dom.style.transition = 'none'//dom.classList.remove('active')cancelAnimationFrame(rafId)})}}
}
 flip 动画思路f - first 记录动画开始前的位置、大小等信息 ( translateY(0px) )l - last  记录动画结束时的位置、大小等信息 ( translateY(100px) )i - invert 对动画前后数据信息的计算(translateY --> 100px,同时利用translate等操作,将dom恢复到 first位置)p - play 开始动画,并移除 i 步骤恢复至 first 的操作,启用tansition,动画就开始了整个过程其实就是,先记录好动画前后的dom位置等数据信息然后,利用css将dom恢复至初始位置最后,移除上一步恢复的状态(此时dom会自动回到last位置,只不过没有过渡效果,生硬的闪现),添加过渡效果,完成动画

完整代码:

const list = document.querySelector('.list')const item = document.querySelectorAll('.list_item')let  sourceNode; list.ondragstart = e =>{sourceNode = e.targetrecord(item)setTimeout(()=>{e.target.classList.add('moving')},0)e.dataTransfer.effectAllowed = 'move'}list.ondragover = e => {e.preventDefault()}list.ondragenter = e =>{e.preventDefault()if(e.target === list || e.target === sourceNode){return false}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)}last([e.target,sourceNode])}list.ondragend = e =>{e.target.classList.remove('moving')}function record(eleAll) {for( let i = 0;i < eleAll.length; i++ ) {const { top,left } = eleAll[i].getBoundingClientRect()eleAll[i]._top_ = topeleAll[i]._left_ = left}
}function last(eleAll) {for( let i = 0;i < eleAll.length; i++ ) {const dom = eleAll[i]const { top,left } = dom.getBoundingClientRect()if(dom._left_) {dom.style.transform = `translate3d(${ dom._left_ - left }px, ${ dom._top_ - top }px,0px)`let rafId = requestAnimationFrame(function() {dom.style.transition = 'transform 300ms ease-out'dom.style.transform = 'none'})dom.addEventListener('transitionend', () => {dom.style.transition = 'none'cancelAnimationFrame(rafId)})}}
}

拖拽页面元素+flip动画的案例相关推荐

  1. 拖拽删除元素、拖拽排序、拖拽预览图片和拖拽移动元素

    介绍 HTML5 提供了专门的拖拽与拖放的 API,目前各浏览器都已支持,包括 IE.HTML 拖放(Drag and Drop)接口使应用程序能够在浏览器中使用拖放功能.例如,用户可使用鼠标选择可拖 ...

  2. UEditor 解决拖拽视频元素改变视频尺寸时,无法保存视频尺寸问题的解决方法

    UEditor虽然强大,但是bug还是蛮多的.比如插入视频元素后,拖拽视频去缩放尺寸,编辑器并没有将实际的尺寸保存下来.当你点击HTML按钮查看源代码时,width和height还是原来的值,再次点击 ...

  3. html拖拽页面特效,div+css实现网页模块或栏目拖动(即拖拽效果)

    //为Number增加一个属性,判断当前数据类型是否是数字 Number.prototype.NaN0=function(){return isNaN(this)?0:this;} //全局变量 va ...

  4. 原生drag拖拽后元素过大,挡住其他可拖动位置无法拖动问题

    写一个蒙层,还未拖动前原始层在上面, 拖动那过程中(dragover)原始层在下面, 拖进目标元素后(drop),此时蒙层在上面,根据drop的$event获取落在蒙层哪个div上,然后放进原始层相应 ...

  5. HTML5:一个拖拽网页元素的例子

    关键字:HTML5, Drag&Drop, JavaScript, CSS 运行环境:Chrome <!DOCTYPE html><html><head>& ...

  6. QML拖拽GridView元素重新排序示例

    import QtQuick 2.6 import QtQuick.Window 2.2Window {visible: truewidth: 1024height: 480title: qsTr(& ...

  7. HTML5原生拖拽/拖放 Drag Drop 详解

    转载自:juejin.im/post/5a169d- 前言 拖放(drap && drop)在我们平时的工作中,经常遇到.它表示:抓取对象以后拖放到另一个位置.目前,它是HTML5标准 ...

  8. HTML5原生拖拽/拖放(drag drop)详解

    前言 拖放(drap && drop)在我们平时的工作中,经常遇到.它表示:抓取对象以后拖放到另一个位置.目前,它是HTML5标准的一部分.我从几个方面学习并实践这个功能. 拖放的流程 ...

  9. 怎么用javascript进行拖拽(转摘)

    本文译自:http://www.webreference.com/programming/javascript/mk/column2/index.html 所有版权归原文所有 由sohotx.com雪 ...

最新文章

  1. Datawhale数据分析教程来了!
  2. 计算机二级日月潭操作步骤,计算机二级考试真题-PPT-文小雨-台湾日月潭介绍
  3. 3.定义一个有10个元素的数组,用其代表10个学生的考试成绩,从键盘输入10个成绩,统计平均成绩。
  4. NIST发布企业移动应用安全建议参考指南
  5. JQuery学习笔记12——GET请求和POST请求
  6. 1070 结绳 (25 分
  7. Quora使用到的技术
  8. Fiddler安装教程(图文版)
  9. C++11 继承构造函数与委托构造函数
  10. 导出期刊对应格式的参考_3.2怎样按照某个期刊的格式要求生成文后的参考文献.PDF...
  11. 华为手机计算机快捷设置密码,华为手机首次重启绘制图案密码后让输入解锁密码。我没有设置呀?怎么办...
  12. 直接收藏-超级好用的国内色彩搭配网站
  13. Cadence用于版图设计时芯片logo的制作
  14. disallow api.php,爬虫之robots.txt
  15. Java三目运算符 (Ternary Operator ? :)
  16. 如何判断高估还是低估
  17. acwing算法基础课——差分
  18. 计算机一级考试:选择题汇总D(精简版)
  19. AForge.Video.FFMPEG库使用注意事项
  20. MB10F-ASEMI整流桥MB10F

热门文章

  1. Guitar Pro钜惠来袭,三折战双“11”
  2. msp430G2553串口通信_launchpad串口通信
  3. 图书馆管理系统程序测试计划
  4. luo3372线段树模板的分块做法
  5. 了解ResNet网络结构特点,利用ResNet完成图像分类
  6. macd和stoch指标组合使用技巧
  7. 架构高性价比的分布式计算机集群
  8. 微信小程序开发入门教程-小程序账号注册及开通
  9. c语言黑洞数习题,18.12.09-C语言练习:黑洞数 / Kaprekar问题
  10. 前端基础-CSS阴影