<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body,ul{
margin: 0;
padding: 0;
}
ul,ul li{
list-style-type: none;
}
body{
height: 1800px;
}
#list{
width: 400px;
margin: 0 auto;
margin-top: 100px;
border: 1px dashed blue;
padding: 10px;
box-sizing: border-box;
}
.item{
padding: 10px;
}
.item:not(:last-child){
margin-bottom: 10px;
}
.ani {
animation: ani 3s ease-in-out 1s both;
}
@keyframes ani {
from {
color: red;
}
to{
color: blue;
}
}
</style>
</head>
<body>
<div id="list">
<ul>
<li id="item1" class="item" draggable="true" style="background: linear-gradient(to right, red, blue);">1水电费水电费,收到烦死了,乐山大佛</li>
<li class="item" draggable="true" style="background-color: rgb(18, 206, 96);">2</li>
<li class="item" draggable="true" style="background-color: rgb(163, 68, 226);">3</li>
<li class="item" draggable="true" style="background-color: rgb(241, 135, 73);">4</li>
<li class="item" draggable="true" style="background-color: rgb(56, 129, 240);">5</li>
</ul>
</div>
<script>
const items = document.querySelectorAll('.item');
const list = document.querySelector('#list');
Function.prototype.expandMethod = function(name,handle){
!this.prototype[name] && (this.prototype[name] = handle)
return this;
}
HTMLElement.expandMethod('onReachBottom', function(callback, options = { threshold: 1 }) {
let containerEl = this;
const isHtmlTag = (el) => Object.prototype.toString.call(el) === '[object HTMLHtmlElement]'
const throttle = (handle, wait = 500) => {
let timer ;
return function(...args){
if(timer){return}
timer = setTimeout(()=>{
handle.apply(this, args);
timer = null;
}, wait);
}
}
window.addEventListener('load', () => {
const reachbottom = new CustomEvent("reachbottom", {
detail:{
...options
}
});
containerEl.addEventListener("reachbottom", (e) => {
let target = e.target;
const threshold = e.detail.threshold;
if(target === window) {
target = document.documentElement;
}
if((target.clientHeight + target.scrollTop) * threshold >= target.scrollHeight) {
typeof callback === 'function' && callback.call(e.target, e);
}
});
(isHtmlTag(containerEl) ? window : containerEl).addEventListener('scroll', throttle((e) => containerEl.dispatchEvent(reachbottom)));
})
})
HTMLElement.expandMethod('getRect', function(refEl) {
const el = this;
const elRect = el.getBoundingClientRect();
const refElRect = refEl.getBoundingClientRect();
return {
top: elRect.top - refElRect.top,
bottom: elRect.bottom - refElRect.top,
left: elRect.left - refElRect.left,
right: elRect.right - refElRect.left,
height: elRect.height,
width: elRect.width,
}
});
function each(arr, callback) {
const keys = Object.keys(arr);
for(var i = 0; i < keys.length; i++) {
if(callback.call(arr, arr[i], i) === false) {
break;
}
}
}
function on(els, eventName, callback) {
if(!Reflect.has(els, 'length')) {
els = [els]
}
each([...els], (el) => el.addEventListener(eventName, callback))
}
function insertAfter(newNode, targetNode) {
let parent = targetNode.parentNode;
if(parent.lastElementChild === targetNode) {
target.parentNode.appendChild(newNode)
}else {
parent.insertBefore(newNode, targetNode.nextElementSibling)
}
}
function ani(els) {
const oldRects = [...els].map((el) => el.getBoundingClientRect())
return {
play() {
const lastRects = [...els].map((el) => el.getBoundingClientRect());
each(lastRects, (lastRect, i) => {
const y = oldRects[i].top - lastRect.top;
if(y) {
const ani = els[i].animate([
{
transform: `translateY(${y}px)`
},
{
transform: `translateY(0)`
}
], {
duration: 300,
easing: 'cubic-bezier(0,0,0.32,1)',
})
ani.finished.then(() => oldRects[i] = els[i].getBoundingClientRect())
}
})
}
}
}
function initDirectionY(lastY) {
let direction = 'none'
return function(curY) {
curY - lastY > 0 && (direction = 'down')
curY - lastY < 0 && (direction = 'up')
lastY = curY;
return direction
}
}
let source = null;
let target = null;
let getDirectionY;
let directionY = 'none';
let flip = null;
on(list, 'dragstart', (e) => {
console.log('dragestart', e);
source = e.target;
e.dataTransfer.effectAllowed = 'move';
getDirectionY = initDirectionY(e.clientY);
flip = ani(items);
})
on(list, 'dragend', (e) => {
console.log('dragend', e);
});
on(list, 'dragenter', (e) => {
e.preventDefault();
if(e.target === source || e.target.getAttribute('draggable') !== "true") {return}
directionY = getDirectionY(e.clientY)
target = e.target;
if(directionY === 'up') {
target.insertAdjacentElement('beforebegin', source);
}else {
target.insertAdjacentElement('afterend', source);
}
flip.play();
});
on(list, 'dragover', (e) => {
e.preventDefault();
})
7
on(list, 'drop', (e) => {
e.preventDefault();
});
document.querySelector('#list').onReachBottom((e) => {
});
</script>
</body>
</html>