Flip动画模块
flip动画是借助浏览器异步渲染的特性设计的一种动画思想,通常用在拖拽布局或拖拽树分支当中。
前言
当节点数量或位置发生变化后,此时立即发生回流行为reflow
,回流内容之一便是重新获得节点的几何特征,比如相对视窗或浏览器的边距,但是接着浏览器并不是同步渲染节点(这里指的渲染节点是指被用户直接看到节点已经发生变化),而是会异步渲染;而Flip
动画是利用浏览器异步渲染的原理来实现的。
Flip动画的基本做法步骤如下:
- 首先记录所有关联节点的几何数据(getBoundingClientRect())中上边距和左边距。(first)
- 使用append、appendChild或insertBefore等方法改变节点中相对位置。
- 再次获得所有关联节点回流后的的几何数据,主要是上边距和左边距。(last)
- 计算回流前后的位置差值,使用transform.translate方法让节点再退回最初的位置。(invert)
- 执行transition动画,让translate归零。(play)
- 动画执行完毕,则所有节点都会回到预想的位置。
Flip动画本质为一个障眼法,但是体验很不错。
简单使用
本Flip模块需要搭配其他模块或组件使用,也可手动编写部分js来实现Flip动画。
基本的做法步骤是:
- 创建
Flip
实例,获得初始位置,通过参数children
获得flip节点。 - 使用
playAll
方法来执行动画。
children的写法同getEls函数的第一个参数,可以是一个节点选择器,也可以是节点数组。
- 输出
- HTML
- JS
-
- 1
- 2
- 3
- 4
- 5
-
-
let btn = document.querySelector('#demo0001btn01'), wrap = document.querySelector('#demo0001'), children = [...wrap.children]; btn.onclick = ()=>{ //创建Flip实例,获得初始位置 let ins = new ax.Flip({children}); //随机位置(洗牌) children.sort(()=>Math.random()-0.5); //按新的位置更新DOM wrap.innerHTML = ''; wrap.append(...children); //执行动画 ins.playAll(); }
通过拖拽改变一个节点位置
通过拖拽改变节点的位置,也就是我们常说的拖拽布局,我们借助原生的拖拽事件完成本例,关于drag的原生事件可查看MDN。
F
(first)L
(last)I
(invert)P
(play)这四个步骤缺一不可。
- 输出
- HTML
- JS
-
- 1.可拖拽
- 2.可拖拽
- 3.可拖拽
- 4.可拖拽
- 5.可拖拽
-
-
let wrap = document.querySelector('#demo0005'), children = [...wrap.children], ins = new ax.Flip({children},false), sourceNode; wrap.ondragstart = (e) => { sourceNode = e.target; e.dataTransfer.effectAllowed = 'move'; //初始化,获得flip节点和他们的初始位置(重要!) ins.init(); }; wrap.ondragover = (e) => { e.preventDefault(); }; wrap.ondragenter = (e) => { e.preventDefault(); //落在wrap节点和自身上则不执行 if (e.target === wrap || e.target === sourceNode) return; //动画中则不触发,使用本模块的节点会自动加上flip属性,该属性记录该节点的flip状态(重要!) if (e.target?.flip?.playing) return; //因为每一次拖拽后都会改变子节点的相对位置,所以每次都需要重新获取新顺序的节点数组 let children = [...wrap.children], sourceIdx = children.indexOf(sourceNode), targetIdx = children.indexOf(e.target); if (sourceIdx < targetIdx) { //如果e.target.nextElementSibling为null,那么这一句相当于wrap.appendChild(sourceNode) wrap.insertBefore(sourceNode, e.target.nextElementSibling); } else { wrap.insertBefore(sourceNode, e.target); } //经过其他子节点时,执行动画(重要!) ins.playAll(); };
基础配置
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
parent | string | '' | 父容器选择器 |
children | string/Element[] | '' | 子节点选择器 |
prevent | boolean | false | 是否禁止play |
duration | number | 300 | flip动画时长,单位ms |
transition | string | '' | 自定义应用transition动画的的css属性 |
等待函数
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
b4Play | function | null | 所有节点play之前执行 |
回调函数
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
onPlayed | function | null | 单个节点play完成时回调 |
onPlayedAll | function | null | 所有节点play完成时回调 |