我们常常会通过修改元素的top,left,translate来其的位置发生改变。
由于修改一个元素top/left值会引起页面重绘,而translate不会,因此从性能优化上来判断,我们会优先使用translate属性。
如何获取当前浏览器支持的transform兼容写法
[‘transform’, ‘webkitTransform’, ‘MozTransform’, ‘msTransform’, ‘OTransform’]1
// 获取当前浏览器支持的transform兼容写法
function getTransform() {
var transform = '',
divStyle = document.createElement('div').style,
// 可能涉及到的几种兼容性写法,通过循环找出浏览器识别的那一个
transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform'],
i = 0,
len = transformArr.length;
for(; i < len; i++) {
if(transformArr[i] in divStyle) {
// 找到之后立即返回,结束函数
return transform = transformArr[i];
}
}
// 如果没有找到,就直接返回空字符串
return transform;
}
当前浏览器并不支持transform,这个时候我们就需要使用left,top值来改变元素的位置。如果支持,就改变transform的值。
如何获取元素的初始位置
1 | function getStyle(elem, property) { // ie通过currentStyle来获取元素的样式,其他浏览器通过getComputedStyle来获取 return document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(elem, false)[property] : elem.currentStyle[property]; } |
获取目标元素初始位置1
function getTargetPos(elem) {
var pos = {x: 0, y: 0};
var transform = getTransform();
if(transform) {
var transformValue = getStyle(elem, transform);
if(transformValue == 'none') {
elem.style[transform] = 'translate(0, 0)';
return pos;
} else {
var temp = transformValue.match(/-?\d+/g);
return pos = {
x: parseInt(temp[4].trim()),
y: parseInt(temp[5].trim())
}
}
} else {
if(getStyle(elem, 'position') == 'static') {
elem.style.position = 'relative';
return pos;
} else {
var x = parseInt(getStyle(elem, 'left') ? getStyle(elem, 'left') : 0);
var y = parseInt(getStyle(elem, 'top') ? getStyle(elem, 'top') : 0);
return pos = {
x: x,
y: y
}
}
}
}
在拖拽过程中,我们需要不停的设置目标元素的新位置1
// pos = { x: 200, y: 100 }
function setTargetPos(elem, pos) {
var transform = getTransform();
if(transform) {
elem.style[transform] = 'translate('+ pos.x +'px, '+ pos.y +'px)';
} else {
elem.style.left = pos.x + 'px';
elem.style.top = pos.y + 'px';
}
return elem;
}
需要用到哪些事件?
mousedown 鼠标按下时触发
mousemove 鼠标按下后拖动时触发
mouseup 鼠标松开时触发
而在移动端则是touchstart、touchmove、touchend。
拖拽的原理
移动后的鼠标位置 - 鼠标初始位置 = 移动后的目标元素位置 - 目标元素的初始位置
移动后目标元素的位置 = dis + 目标元素的初始位置
而在鼠标松开(mouseup)结束拖拽时,我们需要处理一些收尾工作。详情见代码。
1 | // 获取目标元素对象 var oElem = document.getElementById('target'); // 声明2个变量用来保存鼠标初始位置的x,y坐标 var startX = 0; var startY = 0; // 声明2个变量用来保存目标元素初始位置的x,y坐标 var sourceX = 0; var sourceY = 0; part2、功能函数 因为之前已经贴过代码,就不再重复 // 获取当前浏览器支持的transform兼容写法 function getTransform() {} // 获取元素属性 function getStyle(elem, property) {} // 获取元素的初始位置 function getTargetPos(elem) {} // 设置元素的初始位置 function setTargetPos(elem, potions) {} part3、声明三个事件的回调函数 这三个方法就是实现拖拽的核心所在,我将严格按照上面思维导图中的步骤来完成我们的代码。 // 绑定在mousedown上的回调,event为传入的事件对象 function start(event) { // 获取鼠标初始位置 startX = event.pageX; startY = event.pageY; // 获取元素初始位置 var pos = getTargetPos(oElem); sourceX = pos.x; sourceY = pos.y; // 绑定 document.addEventListener('mousemove', move, false); document.addEventListener('mouseup', end, false); } function move(event) { // 获取鼠标当前位置 var currentX = event.pageX; var currentY = event.pageY; // 计算差值 var distanceX = currentX - startX; var distanceY = currentY - startY; // 计算并设置元素当前位置 setTargetPos(oElem, { x: (sourceX + distanceX).toFixed(), y: (sourceY + distanceY).toFixed() }) } function end(event) { document.removeEventListener('mousemove', move); document.removeEventListener('mouseup', end); // do other things } |
1 | (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(); }, // 稍作改造,仅用于获取当前元素的属性,类似于getName getStyle: function(property) { return document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(this.elem, false)[property] : this.elem.currentStyle[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; }, // 用来设置当前元素的位置 setPostion: 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 currentX = event.pageX; var currentY = event.pageY; var distanceX = currentX - self.startX; var distanceY = currentY - self.startY; self.setPostion({ x: (self.sourceX + distanceX).toFixed(), y: (self.sourceY + distanceY).toFixed() }) } function end(event) { document.removeEventListener('mousemove', move); document.removeEventListener('mouseup', end); // do other things } } } // 私有方法,仅仅用来获取transform的兼容写法 function getTransform() { var transform = '', divStyle = document.createElement('div').style, transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform'], i = 0, len = transformArr.length; for(; i < len; i++) { if(transformArr[i] in divStyle) { return transform = transformArr[i]; } } return transform; } // 一种对外暴露的方式 window.Drag = Drag; })(); // 使用:声明2个拖拽实例 new Drag('target'); |