ele-drag
Version:
一个简单易用的js拖动库
221 lines (188 loc) • 7.12 kB
JavaScript
/**
* Dragable - 简单易用的拖动库
* 支持移动端和电脑端
* 自动添加到HTMLElement原型
*
* 使用方法:
* element.enableDrag();
* element.disableDrag();
*/
(function() {
// 存储拖动状态
const dragStateMap = new WeakMap();
/**
* 初始化拖动状态
* @param {HTMLElement} element
*/
function initDragState(element) {
dragStateMap.set(element, {
isDragging: false,
startX: 0,
startY: 0,
startLeft: 0,
startTop: 0,
touchId: null // 用于跟踪多点触控中的特定触点
});
}
/**
* 处理拖动开始
* @param {HTMLElement} element
* @param {Event} e
*/
function handleDragStart(element, e) {
const state = dragStateMap.get(element);
if (!state) return;
// 阻止默认行为以避免不必要的页面滚动或选择
e.preventDefault();
// 获取初始位置
let clientX, clientY;
if (e.type.includes('touch')) {
const touch = e.touches[0] || e.changedTouches[0];
clientX = touch.clientX;
clientY = touch.clientY;
state.touchId = touch.identifier;
} else {
clientX = e.clientX;
clientY = e.clientY;
}
// 获取元素当前样式
const style = window.getComputedStyle(element);
const left = parseFloat(style.left) || 0;
const top = parseFloat(style.top) || 0;
// 更新状态
state.isDragging = true;
state.startX = clientX;
state.startY = clientY;
state.startLeft = left;
state.startTop = top;
// 添加拖动类
element.classList.add('dragging');
// 触发自定义事件
const event = new CustomEvent('dragstart', {
detail: { x: clientX, y: clientY }
});
element.dispatchEvent(event);
}
/**
* 处理拖动移动
* @param {HTMLElement} element
* @param {Event} e
*/
function handleDragMove(element, e) {
const state = dragStateMap.get(element);
if (!state || !state.isDragging) return;
e.preventDefault();
let clientX, clientY;
if (e.type.includes('touch')) {
// 查找匹配的触点
let touch;
if (e.touches) {
for (let i = 0; i < e.touches.length; i++) {
if (e.touches[i].identifier === state.touchId) {
touch = e.touches[i];
break;
}
}
}
if (!touch && e.changedTouches) {
for (let i = 0; i < e.changedTouches.length; i++) {
if (e.changedTouches[i].identifier === state.touchId) {
touch = e.changedTouches[i];
break;
}
}
}
if (!touch) return;
clientX = touch.clientX;
clientY = touch.clientY;
} else {
clientX = e.clientX;
clientY = e.clientY;
}
// 计算新位置
const deltaX = clientX - state.startX;
const deltaY = clientY - state.startY;
const newLeft = state.startLeft + deltaX;
const newTop = state.startTop + deltaY;
// 应用新位置
element.style.left = `${newLeft}px`;
element.style.top = `${newTop}px`;
// 触发自定义事件
const event = new CustomEvent('dragmove', {
detail: { x: clientX, y: clientY, left: newLeft, top: newTop }
});
element.dispatchEvent(event);
}
/**
* 处理拖动结束
* @param {HTMLElement} element
* @param {Event} e
*/
function handleDragEnd(element, e) {
const state = dragStateMap.get(element);
if (!state || !state.isDragging) return;
e.preventDefault();
state.isDragging = false;
element.classList.remove('dragging');
// 触发自定义事件
const event = new CustomEvent('dragend');
element.dispatchEvent(event);
}
/**
* 启用拖动功能
*/
HTMLElement.prototype.enableDrag = function() {
// 确保元素可以定位
if (window.getComputedStyle(this).position === 'static') {
this.style.position = 'relative';
}
// 初始化状态
initDragState(this);
// 添加事件监听器
this.addEventListener('mousedown', this._dragStartHandler = (e) => handleDragStart(this, e));
this.addEventListener('touchstart', this._dragStartHandlerTouch = (e) => handleDragStart(this, e), { passive: false });
document.addEventListener('mousemove', this._dragMoveHandler = (e) => handleDragMove(this, e));
document.addEventListener('touchmove', this._dragMoveHandlerTouch = (e) => handleDragMove(this, e), { passive: false });
document.addEventListener('mouseup', this._dragEndHandler = (e) => handleDragEnd(this, e));
document.addEventListener('touchend', this._dragEndHandlerTouch = (e) => handleDragEnd(this, e));
document.addEventListener('touchcancel', this._dragEndHandlerTouchCancel = (e) => handleDragEnd(this, e));
};
/**
* 禁用拖动功能
*/
HTMLElement.prototype.disableDrag = function() {
// 移除事件监听器
if (this._dragStartHandler) {
this.removeEventListener('mousedown', this._dragStartHandler);
this._dragStartHandler = null;
}
if (this._dragStartHandlerTouch) {
this.removeEventListener('touchstart', this._dragStartHandlerTouch);
this._dragStartHandlerTouch = null;
}
if (this._dragMoveHandler) {
document.removeEventListener('mousemove', this._dragMoveHandler);
this._dragMoveHandler = null;
}
if (this._dragMoveHandlerTouch) {
document.removeEventListener('touchmove', this._dragMoveHandlerTouch);
this._dragMoveHandlerTouch = null;
}
if (this._dragEndHandler) {
document.removeEventListener('mouseup', this._dragEndHandler);
this._dragEndHandler = null;
}
if (this._dragEndHandlerTouch) {
document.removeEventListener('touchend', this._dragEndHandlerTouch);
this._dragEndHandlerTouch = null;
}
if (this._dragEndHandlerTouchCancel) {
document.removeEventListener('touchcancel', this._dragEndHandlerTouchCancel);
this._dragEndHandlerTouchCancel = null;
}
// 移除状态
dragStateMap.delete(this);
// 移除拖动类
this.classList.remove('dragging');
};
})();