1、我们常常会通过修改元素的top,left,translate来其的位置发生改变。修改元素的left,top值,但会引起页面重绘,而transform不会,所以要优先使用transform。
2、如何获取当前浏览器支持的transform兼容写法
3、获取元素的初始位置:
4、需要绑定的事件:mousedown,mousemove,mouseup
而在移动端,分别与之对应的则是touchstart、touchmove、touchend。
5、原理
当事件触发时,我们可以通过事件对象获取到鼠标的精切位置。这是实现拖拽的关键。当鼠标按下(mousedown触发)时,我们需要记住鼠标的初始位置与目标元素的初始位置,我们的目标就是实现当鼠标移动时,目标元素也跟着移动,根据常理我们可以得出如下关系:
移动后的鼠标位置 - 鼠标初始位置 = 移动后的目标元素位置 - 目标元素的初始位置
如果鼠标位置的差值我们用dis来表示,那么目标元素的位置就等于:
移动后目标元素的位置 = dis + 目标元素的初始位置通过事件对象,我们可以精确的知道鼠标的当前位置,因此当鼠标拖动(mousemove)时,我们可以不停的计算出鼠标移动的差值,以此来求出目标元素的当前位置。这个过程,就实现了拖拽。
而在鼠标松开(mouseup)结束拖拽时,我们需要处理一些收尾工作。
6、封装原则
如何合理的处理属性与方法的位置。
构造函数中: 属性与方法为当前实例单独拥有,只能被当前实例访问,并且每声明一个实例,其中的方法都会被重新创建一次。
原型中: 属性与方法为所有实例共同拥有,可以被所有实例访问,新声明实例不会重复创建方法。
模块作用域中:属性和方法不能被任何实例访问,但是能被内部方法访问,新声明的实例,不会重复创建相同的方法。
对于方法的判断比较简单。
因为在构造函数中的方法总会在声明一个新的实例时被重复创建,因此我们声明的方法都尽量避免出现在构造函数中。
而如果你的方法中需要用到构造函数中的变量,或者想要公开,那就需要放在原型中。
如果方法需要私有不被外界访问,那么就放置在模块作用域中。
对于属性的判断
对于属性放置于什么位置需要在实际开发中不断的总结经验。
如果属性值只能被实例单独拥有,比如person对象的name,只能属于某一个person实例,又比如这里拖拽对象中,某一个元素的初始位置,也仅仅只是这个元素的当前位置,这个属性,则适合放在构造函数中。
而如果一个属性仅仅供内部方法访问,这个属性就适合放在模块作用域中。
模块封装代码:
; //文件合并时,防止前一个文件末尾没加分号
(function() {
//私有属性,内部使用,不需实例访问
var transform = getTransform();
function Drag(selector) {
this.elem = typeof selector == 'object' ? selector : document.getElementById(selector);
this.startX = 0;
this.startY = 0;
this.sourceX = 0;
this.sourceY = 0;
this.init();
}
Drag.prototype = {
constructor: Drag,
init: function() {
this.setDrag();
},
getStyle: function(property) {
return window.getComputedStyle ? window.getComputedStyle(this.elem, null).getPropertyValue(property) : this.currentStyle.getAttribute(property);
},
//获取当前元素的位置信息,注意与之前的不同之处
getPosition: function() {
var pos = { x: 0, y: 0 };
if (transform) {
var transformValue = this.getStyle(transform);
if (transformValue == 'none') {
this.elem.style[transform] = 'translate(0,0)';
} else {
var temp = transformValue.match(/-?\d+/g);
pos = {
x: parseInt(temp[4].trim()),
y: parseInt(temp[5].trim())
}
}
} else {
if (this.getStyle('position') == 'static') {
this.elem.style.position = 'relative';
} else {
pos = {
x: parseInt(this.getStyle('left') ? this.getStyle('left') : 0),
y: parseInt(this.getStyle('top') ? this.getStyle('top') : 0)
}
}
}
return pos;
},
// 用来设置当前元素的位置
setPosition: function(pos) {
if (transform) {
this.elem.style[transform] = 'translate(' + pos.x + 'px, ' + pos.y + 'px)';
} else {
this.elem.style.left = pos.x + 'px';
this.elem.style.top = pos.y + 'px';
}
},
setDrag: function() {
var self = this;
this.elem.addEventListener('mousedown', start, false);
function start(event) {
//鼠标初始位置
self.startX = event.pageX;
self.startY = event.pageY;
var pos = self.getPosition();
self.sourceX = pos.x;
self.sourceY = pos.y;
document.addEventListener('mousemove', move, false);
document.addEventListener('mouseup', end, false);
}
function move(event) {
// 获取鼠标当前位置
var curX = event.pageX;
var curY = event.pageY;
// 计算差值
var disX = curX - self.startX;
var disY = curY - self.startY;
self.setPosition({
x: (self.sourceX + disX).toFixed(),
y: (self.sourceY + disY).toFixed()
});
}
function end(event) {
document.removeEventListener('mousemove', move);
document.removeEventListener('mouseup', end);
//do other things
}
}
}
//私有方法
function getTransform() {
var transform = '',
divStyle = document.createElement('div').style,
transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'oTransform'];
//通过循环找出浏览器识别的那个,in操作符用于判断是否识别
for (var i = 0, len = transformArr.length; i < len; i++) {
if (transformArr[i] in divStyle) {
return transform = transformArr[i];
}
}
return transform;
}
window.Drag = Drag;
})();
new Drag('target');
new Drag('target2');
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
#target,
#target2 {
width: 50px;
height: 50px;
background-color: orange;
cursor: move;
}
#target2 {
background-color: red;
}
</style>
</head>
<body>
<div style="height:1000px">hha</div>
<div id="target"></div>
<div id="target2"></div>
</body>
</html>
jquery实例方法扩展:
// 通过扩展方法将拖拽扩展为jQuery的一个实例方法
(function ($) {
$.fn.extend({
becomeDrag: function () {
new Drag(this[0]);
return this; // 注意:为了保证jQuery所有的方法都能够链式访问,每一个方法的最后都需要返回this,即返回jQuery实例
}
})
})(jQuery);
参考链接:http://www.jianshu.com/p/b3dee0e84454
http://www.jianshu.com/p/3f97570d22b4