拖拽页面元素+flip动画的案例
创始人
2024-01-25 08:07:17
0

先上效果: 

实现思路和流程:

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

 基础页面布局:

1
2
3
4
5

实现拖动步骤

获取到总的外容器 便于下面事件委托
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)})}}
}

相关内容

热门资讯

加盟茶颜悦色奶茶店大概要多少钱... 现代中国茶叶店|店铺开业|咨询|申请|汇鑫|洛尔|备注现代中国茶叶店可以先审批年初,武汉开店计划因疫...
2020年夫妻适合的小店(20... 现在人们的经济压力越来越大,很多夫妻不愿意为别人打工,就想自己创业。毕竟,夫妻利益是一致的!但是什么...
在深圳适合一个人做的小生意,有... 现在的年轻人和中年朋友都会有创业的心,因为创业就是自由,自由工作。步入正轨后,创业的收入将超过上班的...
两元加盟店全国连锁(两元加盟店... 前阵子拼多多成功上市后,圈内有大佬分析拼多多的对手会是那些物美价廉的线下商品市场,比如地摊、跳蚤市场...
2021年一个人创业做什么项目... 2021年到底还有哪些好的创业项目呢?建议不要投资实体店。如果你现在去加盟实体店,初装费贵,人力和精...
创业板50指数成分股(创业板5... 每周市场战略观点,今晚贴一篇文章,谢谢支持!这两周外资积极流入,重点龙头股季报超预期,市场持续反弹。...
小吃加盟排行榜最火,最火的加盟... 新年伊始,就快到年底了。在这个特殊的日子里,中国加盟网结合行业动态、品牌实力、市场信誉等各个维度,编...
海景房直播暴露我军舰动态,国家...   5日,“国家安全部”微信公众号公布一则用军港当背景直播暴露我军舰动态的案例:
身无分文黑户怎样快速翻身(30... 一个人三十岁,欠了几万的债。他的人生毁了吗?有这种想法的人,你已经被废了。我以为我欠了几百万,几千万...
2021年加盟好项目,2021... 专注创业,让你不再孤独的小鸽子又来了。上一次小鸽子在后台默默回答是小哥哥还是小姐姐的问题后,有热心创...