Draggable Elements 元素拖拽功能实现代码

当然我们可以研究js库的源码, 也可以自己去发明轮子试试看, 其过程还是挺有趣的...下面我就来实现下页面元素的拖拽功能
现在就开始着手实现, 让我们从最顶层的方法讲起, 它用于初始化一个drag object, 方法的声明如下
function DragObject(cfg)
这里的cfg我们用一个对象来传入, 有点像Extjs里配置属性

复制代码 代码如下:


var dragObj = new DragObject({
el: 'exampleB',
attachEl: 'exampleBHandle',
lowerBound: new Position(0, 0), //position代表一个点,有属性x,y下面会详细讲到
upperBound: new Position(500, 500),
startCallback: ..., // 开始拖拽时触发的回调 这里均省略了
moveCallback: ..., // 拖拽过程中触发的回调
endCallback: ..., // 拖拽结束触发的回调
attachLater: ... // 是否立刻启动拖拽事件的监听
});


配置参数中el可以是具体元素的id, 也可以直接是个dom对象 attachEl就是例子中的handle元素, 通过拖拽它来拖拽元素, lowerBound和upperBound是用于限定拖拽范围的, 都是Position对象, 关于这个对象的封装和作用我们下面会分析到,不急哈: ), 如果没有传入的话, 拖拽的范围就没有限制. startCallback, moveCallback, endCallback都是些回调函数, attachLater为true或者false. 如果不是很明白看了下面的分析, 我想你肯定很快会清楚的..
下面就来写Position, 代码如下:

复制代码 代码如下:


function Position(x, y) {
this.X = x;
thix.Y = y;
}
Position.prototype = {
constructor: Position,
add : function(val) {
var newPos = new Position(this.X, this.Y);
if (val) {
newPos.X += val.X;
newPos.Y += val.Y;
}
return newPos;
},
subtract : function(val) {
var newPos = new Position(this.X, this.Y);
if (val) {
newPos.X -= val.X;
newPos.Y -= val.Y;
}
return newPos;
},
min : function(val) {
var newPos = new Position(this.X, this.Y);
if (val) {
newPos.X = this.X > val.X ? val.X : this.X;
newPos.Y = this.Y > val.Y ? val.Y : this.Y;
return newPos;
}
return newPos;
},
max : function(val) {
var newPos = new Position(this.X, this.Y);
if (val) {
newPos.X = this.X < val.X ? val.X : this.X;
newPos.Y = this.Y < val.Y ? val.Y : this.Y;
return newPos;
}
return newPos;
},
bound : function(lower, upper) {
var newPos = this.max(lower);
return newPos.min(upper);
},
check : function() {
var newPos = new Position(this.X, this.Y);
if (isNaN(newPos.X))
newPos.X = 0;
if (isNaN(newPos.Y))
newPos.Y = 0;
return newPos;
},
apply : function(el) {
if(typeof el == 'string')
el = document.getElementById(el);
if(!el) return;
el.style.left = this.X + 'px';
el.style.top = this.Y + 'px';
}
};


一个坐标点的简单封装, 它保存两个值: x, y坐标. 我们能够通过add和substract方法跟别的坐标点进行+运算和-运算, 返回一个计算过的新坐标点. min和max函数顾名思义用于跟其他坐标点进行比较,并返回其中较小和教大的值.bound方法返回一个在限定范围内的坐标点. check方法用于确保属性x, y的值是数字类型的, 否则会置0. 最后apply方法就是把属性x,y作用于元素style.left和top上. 接着我把剩下的大部分代码拿出来, 再一点一点看:

复制代码 代码如下:


function DragObject(cfg) {
var el = cfg.el,
attachEl = cfg.attachEl,
lowerBound = cfg.lowerBound,
upperBound = cfg.upperBound,
startCallback = cfg.startCallback,
moveCallback = cfg.moveCallback,
endCallback = cfg.endCallback,
attachLater = cfg.attachLater;
if(typeof el == 'string')
el = document.getElementById(el);
if(!el) return;
if(lowerBound != undefined && upperBound != undefined) {
var tempPos = lowerBound.min(upperBound);
upperBound = lowerBound.max(upperBound);
lowerBound = tempPos;
}
var cursorStartPos,
elementStartPos,
dragging = false,
listening = false,
disposed = false;
function dragStart(eventObj) {
if(dragging || !listening || disposed) return;
dragging = true;
if(startCallback)
startCallback(eventObj, el);
cursorStartPos = absoluteCursorPosition(eventObj);
elementStartPos = new Position(parseInt(getStyle(el, 'left')), parseInt(getStyle(el, 'top')));
elementStartPos = elementStartPos.check();
hookEvent(document, 'mousemove', dragGo);
hookEvent(document, 'mouseup', dragStopHook);
return cancelEvent(eventObj);
}
function dragGo(e) {
if(!dragging || disposed) return;
var newPos = absoluteCursorPosition(e);
newPos = newPos.add(elementStartPos)
.subtract(cursorStartPos)
.bound(lowerBound, upperBound);
newPos.apply(el);
if(moveCallback)
moveCallback(newPos, el);
return cancelEvent(e);
}
function dragStopHook(e) {
dragStop();
return cancelEvent(e);
}
function dragStop() {
if(!dragging || disposed) return;
unhookEvent(document, 'mousemove', dragGo);
unhookEvent(document, 'mouseup', dragStopHook);
cursorStartPos = null;
elementStartPos = null;
if(endCallback)
endCallback(el);
dragging = false;
}
this.startListening = function() {
if(listening || disposed) return;
listening = true;
hookEvent(attachEl, 'mousedown', dragStart);
};
this.stopListening = function(stopCurrentDragging) {
if(!listening || disposed)
return;
unhookEvent(attachEl, 'mousedown', dragStart);
listening = false;
if(stopCurrentDragging && dragging)
dragStop();
};
this.dispose = function() {
if(disposed) return;
this.stopListening(true);
el = null;
attachEl = null;
lowerBound = null;
upperBound = null;
startCallback = null;
moveCallback = null;
endCallback = null;
disposed = true;
};
this.isDragging = function() {
return dragging;
};
this.isListening = function() {
return listening;
};
this.isDisposed = function() {
return disposed;
};
if(typeof attachEl == 'string')
attachEl = document.getElementById(attachEl);
// 如果没有配置, 或者没找到该Dom对象, 则用el
if(!attachEl) attachEl = el;
if(!attachLater)
this.startListening();
}


其中一些未给出方法, 在往下分析的过程中, 会一一给出....
我们先通过cfg来使el和attachEl指向实际的Dom对象, 如果attachEl没配置或者没找到对应元素则用el替代. 我们同时设置了一些在拖拽中要用到的变量. cursorStartPos用于保存鼠标按下开始拖拽时鼠标的坐标点. elementStartPos用于保存元素开始拖拽时的起始点. dragging, listening, disposed是一些状态变量. listening: 指drag object是否正在监听拖拽开始事件. dragging: 元素是否正在被拖拽. disposed: drag object被清理, 不能再被拖拽了.
在代码的最后, 我们看到如果attachLater不为true, 那么就调用startListening, 这是一个 public方法定义在drag object中, 让我们看下它的实现

复制代码 代码如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdfdgs.html