当前仓库属于暂停状态,部分功能使用受限,详情请查阅 仓库状态说明
3 Star 10 Fork 0

于占涛 / YuDrag
暂停

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
yudrag.js 13.52 KB
一键复制 编辑 原始数据 按行查看 历史
/**
* Created by yuzhantao on 2017/9/10.
*/
function YuDrag(opt) {
var MOUSE_DOWN = 'mousedown';
var MOUSE_UP = 'mouseup';
var MOUSE_MOVE = 'mousemove';
var TOUCH_START = 'touchstart';
var TOUCH_END = 'touchend';
var TOUCH_MOVE = 'touchmove';
var yudrag = {};
yudrag.dragDatas = { // 拖动相关的数据
srcElement: null, // 拖动的元素
destElement: null, // 拖动到指定目标的元素
};
yudrag.classNames = opt.classNames;
yudrag.isAutoExchangeElement = opt.isAutoExchangeElement ? opt.isAutoExchangeElement : false; // 当拖动到目标元素时,是否自动将原拖动元素和目标元素交换位置
yudrag.longTouchMillisecond = typeof opt.longTouchMillisecond === 'number' ? opt.longTouchMillisecond : 1500; // 屏幕长按的毫秒数
// =====================================================================================================
// 公用方法
// =====================================================================================================
/**
* 获取touch对象
* @param event
* @returns {*}
*/
function getTouch(event) {
var touch;
if (event.touches) {
touch = event.touches[0];
} else {
touch = event;
}
return touch;
}
/**
* 查找ele到ele以上的所有父节点是否包含指定的class名,并返回离ele最近的包含class名的元素。
* @param ele 要查找的ele元素
* @param className 要查找的类名
* @returns {*} 如果找到,返回元素对象;否则返回null;
*/
var findElement = function (ele, className) {
var tmp = ele;
var index = 0;
do {
if (hasClass(tmp, className)) { // 如果查到指定className类名,就返回此元素。
tmp.setAttribute('drag-find-level', index.toString()); // 在查到的元素中添加查找深度,用于在多类名查找中找出离鼠标点击最近的元素。
tmp.setAttribute('drag-class', className); // 在查到的元素中记录查询的类名,作用同上。
return tmp;
}
index++;
} while (tmp = tmp.parentNode);
return null;
}
/**
* 查找ele到ele以上的所有父节点是否包含calssArray里指定的类名,并放回最靠近ele的元素
* @param ele 起始元素对象
* @param classArray 查找的calss名称数组
* @returns {number} 如果找到,返回元素对象;否则返回null;
*/
var findNearElement = function (ele, classNames) {
var findEles = new Array();
// 从ele到ele以上的所有父节点中遍历包含className的元素,并记录到findEles数组中。
for (var i = 0; i < classNames.length; i++) {
var e = findElement(ele, classNames[i]);
if (e) findEles.push(e);
}
// 查找离ele元素最近的一个元素节点,将它作为可拖动对象。
var minEle = 0;
var minVal = 999;
for (var i = 0; i < findEles.length; i++) {
if (minVal > findEles[i].getAttribute('drag-find-level')) {
minVal = findEles[i].getAttribute('drag-find-level');
minEle = findEles[i];
}
}
return minEle;
}
/**
* 元素是否包含指定类名
* @param element 要查找的DOM元素
* @param cls 要查找的类名
* @returns {boolean} 如果元素中的class属性包含cls名,就返回true;否则返回false;
*/
function hasClass(element, cls) {
return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
}
// =====================================================================================================
// 状态机与类的定义
// =====================================================================================================
/**
* 有限状态机(类)
* @constructor
*/
function StateMachine() {
var curState = null;
this.changeState = function (state) {
if (curState) curState.exit();
curState = state;
curState.entry();
}
this.do = function (event) {
if (curState) curState.do(event);
}
}
/**
* 检查长按屏幕的状态(类)
* @param longTouchMillisecond 长按屏幕的毫秒数
* @param stateMachine 状态机
* @constructor
*/
function CheckLongTouchState(yudragObj, stateMachine) {
var sm = stateMachine;
var timeoutId;
var isShellTimeout = false;
this.entry = function () {
}
this.do = function (event) {
if (isShellTimeout) return;
if (event.type == TOUCH_START || event.type == MOUSE_DOWN) {
var touch = getTouch(event);
var ele = document.elementFromPoint(touch.pageX, touch.pageY); // 获取坐标下的元素
yudragObj.dragDatas.srcElement = findNearElement(ele, yudragObj.classNames); // 根据样式名查找最近的一个元素作为可拖动的元素
if (!yudragObj.dragDatas.srcElement) { // 如果没有找到可拖动元素,就返回。
return;
}
ele.ondragstart=function () {
return false;
} // 禁止掉鼠标点击元素的拖动事件
timeoutId = setTimeout(function () {
isShellTimeout = true;
sm.changeState(new DragState(yudragObj, sm, event));
}, yudragObj.longTouchMillisecond);
}else if(event.type==TOUCH_END || event.type==MOUSE_UP){
if(timeoutId) clearTimeout(timeoutId);
timeoutId=null;
}
}
this.exit = function () {
}
}
/**
* 拖动状态(类)
* @param yudrag
* @param stateMachine
* @constructor
*/
function DragState(yudragObj, stateMachine, touchEvent) {
var TEMP_ELEMENT_ID = 'yudrag_temp_element_id';
var sm = stateMachine;
var mouseToSrcElementSpanX, mouseToSrcElementSpanY;
var tempElement;
/**
* 状态入口函数,鼠标长按后,如果点击了设置className的元素,就执行拖动操作;否则返回长按检测状态。
*/
this.entry = function () {
var touch=getTouch(touchEvent); // 获取touch对象
mouseToSrcElementSpanX = touch.offsetX; // 获取鼠标到元素的横向距离
mouseToSrcElementSpanY = touch.offsetY; // 获取鼠标到元素的纵向距离
tempElement = copyElement(yudragObj.dragDatas.srcElement, TEMP_ELEMENT_ID); // 创建临时拖动的数据
setTempElementStyle(tempElement); // 设置临时元素的样式
document.body.appendChild(tempElement); // 添加临时元素到body
setTempElementPosition(
tempElement,
touchEvent.pageX - mouseToSrcElementSpanX,
touchEvent.pageY - mouseToSrcElementSpanY
); // 设置临时元素的位置
}
/**
* 状态执行函数,在鼠标移动时,设置临时元素跟随鼠标移动
* @param event
*/
this.do = function (event) {
if (event.type == TOUCH_END || event.type == MOUSE_UP) {
sm.changeState(new ElementHandlerState(yudragObj, sm)); // 转换到元素处理状态
return;
} // 如果鼠标抬起,转换到元素处理状态。
setTempElementPosition(
tempElement,
event.pageX - mouseToSrcElementSpanX,
event.pageY - mouseToSrcElementSpanY
); // 设置临时元素的位置
// 获取目标元素
var touch=getTouch(event);
var ele = document.elementFromPoint(touch.pageX, touch.pageY); // 获取鼠标下的元素节点
var dragClassName = getDragClassName(yudragObj.dragDatas.srcElement); // 获取原拖拽元素的类名
yudragObj.dragDatas.destElement = findElement(ele,dragClassName); // 根据类名和ele查找目标元素
}
/**
* 状态结束函数,在鼠标抬起时,清除临时元素
*/
this.exit = function () {
var tempElement = document.getElementById(TEMP_ELEMENT_ID); // 获取临时元素
if (tempElement) { // 如果临时元素存在,就删除它。
document.body.removeChild(tempElement); // 清除临时元素
}
}
/**
* 获取拖拽的类名
* @param ele
* @returns {string}
*/
function getDragClassName(ele) {
return ele.getAttribute('drag-class');
}
/**
* 设置临时元素样式
* @param ele
*/
function setTempElementStyle(ele) {
ele.style.margin = '0';
ele.style.padding = '0';
ele.style.borderWidth = '0px';
ele.style.position = 'absolute';
ele.style.zIndex = 999;
ele.style.pointerEvents = 'none';
ele.style.opacity = "0.5";
}
/**
* 设置临时元素的位置
* @param ele 临时元素对象
* @param x 要移动到的x坐标
* @param y 要移动到的y坐标
*/
function setTempElementPosition(ele, x, y) {
ele.style.left = x + "px";
ele.style.top = y + "px";
}
/**
* 复制元素
* @param ele
*/
var copyElement = function (ele, id) {
// 复制一个元素用来拖动,但因为有时候元素的css显示样式依赖父元素,所以我们复制元素时从父元素开始copy
var div = ele.parentNode.cloneNode(true); // 拷贝一个ele的父元素
var ce = ele.cloneNode(true); // 拷贝ele元素
ce.id = id+"_CLIENT_ID"; // 为了方式拷贝元素与ele的id冲突,所以重新设置拷贝元素的id
div.innerHTML = ""; // 清空父元素里的子元素
div.appendChild(ce); // 将拷贝的ele元素添加到父元素中
div.id = id; // 设置拷贝父元素的id,防止id冲突。
return div;
}
}
/**
* 元素处理状态
* @param yudragObj
* @param stateMachine
* @constructor
*/
function ElementHandlerState(yudragObj, stateMachine) {
var sm = stateMachine;
this.entry = function () {
if(yudragObj.isAutoExchangeElement &&
yudragObj.dragDatas.srcElement &&
yudragObj.dragDatas.destElement
){
exchangeElement(yudragObj.dragDatas.srcElement, yudragObj.dragDatas.destElement);
}
sm.changeState(new CheckLongTouchState(yudragObj,sm));
}
this.do = function (event) {
}
this.exit = function () {
}
// 交换两个元素
function exchangeElement(a, b) {
if(a==b)return;
var bp=b.parentNode,ap=a.parentNode; // 记录父元素
var an=a.nextElementSibling,bn=b.nextElementSibling; // 记录下一个同级元素
//如果参照物是邻近元素则直接调整位置
if(an==b)return bp.insertBefore(b,a);
if(bn==a)return ap.insertBefore(a,b);
if(a.contains(b)) //如果a包含了b
return ap.insertBefore(b,a),bp.insertBefore(a,bn);
else
return bp.insertBefore(a,b),ap.insertBefore(b,an);
}
}
// =====================================================================================================
// 实现
// =====================================================================================================
var smf = new StateMachine(); // 实例化状态机
smf.changeState(new CheckLongTouchState(yudrag, smf)); // 给状态机设置默认状态为长按检查状态
// 手机屏幕touch或pc鼠标事件
var touch = function (event) {
// console.log(event);
smf.do(event);
}
document.addEventListener(TOUCH_START, touch);
document.addEventListener(TOUCH_MOVE, touch);
document.addEventListener(TOUCH_END, touch);
document.addEventListener(MOUSE_DOWN, touch);
document.addEventListener(MOUSE_MOVE, touch);
document.addEventListener(MOUSE_UP, touch);
yudrag.destory = function () {
document.removeEventListener(TOUCH_START, touch);
document.removeEventListener(TOUCH_MOVE, touch);
document.removeEventListener(TOUCH_END, touch);
document.removeEventListener(MOUSE_DOWN, touch);
document.removeEventListener(MOUSE_MOVE, touch);
document.removeEventListener(MOUSE_UP, touch);
}
return yudrag;
}
JavaScript
1
https://gitee.com/yuzhantao/JS-YuDrag.git
git@gitee.com:yuzhantao/JS-YuDrag.git
yuzhantao
JS-YuDrag
YuDrag
master

搜索帮助