今天来研究下跟拖拽有关的问题,以备以后可以实现更多友好的交互形式。
1. 相关原生方法:
- ondragstart:拖拽开始
- ondragend:拖拽结束
- ondragenter:拖拽元素进入目标元素头上的时候
- ondrop:拖拽元素进入目标元素头上,同时鼠标松开的时候
- ondragover:拖拽元素在目标元素头上移动的时候
有点晕?按下面的例子分别都操作下就明白啦。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style type= text/css>
.container {
width: 400px;
height: 400px;
background: #ff6632;
}
</style>
</head>
<body>
<div id='drag1' draggable="true">拖拽我,come on!</div>
<div id='drag2'>拽不动我,come on!</div>
<div class='container' id='container'>
</div>
</body>
<script>
document.getElementById('drag1').ondragstart = function(){
console.log('开始拖拽')
}
document.getElementById('drag1').ondragend = function(){
console.log('结束拖拽')
}
document.getElementById('container').ondrop = function(){
console.log('已掉入规定范围')
}
document.getElementById('container').ondragenter = function(){
console.log('拖拽元素进入目标元素头上的时候')
}
document.getElementById('container').ondragover = function(ev) {
//防止ondrop不生效,ev.preventDefault()
ev.preventDefault();
console.log('拖拽元素在目标元素头上移动的时候')
};
</script>
</html>
特别注意:
- 想对一个元素进行一系列的拖拽操作,要注明属性
draggable="true"来激活它的可拖拽性。 - 在使用ondrop时,发现没有生效,在ondragover中需要阻止默认事件的触发。
到这里我们已经可以拖拽元素并且监听一系列的拖拽事件了,然而本文远远没有结束,我们接着往下看:
人家网页上的拖拽都是拖着元素可以随便动,我这写的什么玩意!元素根本都不能动,不好使啊!!!!
别着急,现在我们来让元素跟着拖拽动起来!!!
- 首先我们要搞清楚一些概念:
- 清楚了概念,我们看下具体实现。
html部分:
<style type='text/css'>
#box {
position: absolute;
left: 100px;
top: 100px;
padding: 5px;
background: #f0f3f9;
width: 100px;
height: 100px;
}
</style>
...
<div id='box'></div>
js部分:
window.onload = function () {
var oBox = document.getElementById("box");
startDrag(oBox, oBox);
};
var params = {
left: 0,
top: 0,
currentX: 0,
currentY: 0,
flag: false
};
//获取相关CSS属性
var getCss = function (o, key) {
return o.currentStyle ? o.currentStyle[key] : document.defaultView.getComputedStyle(o, null)[key];
};
//拖拽的实现
var startDrag = function (bar, target, callback) {
if (getCss(target, "left") !== "auto") {
params.left = getCss(target, "left");
}
if (getCss(target, "top") !== "auto") {
params.top = getCss(target, "top");
}
//o是移动对象
bar.onmousedown = function (event) {
params.flag = true;
var e = event;
params.currentX = e.clientX;
params.currentY = e.clientY;
};
document.onmouseup = function () {
console.log('test')
params.flag = false;
if (getCss(target, "left") !== "auto") {
params.left = getCss(target, "left");
}
if (getCss(target, "top") !== "auto") {
params.top = getCss(target, "top");
}
};
document.onmousemove = function (event) {
var e = event ? event : window.event;
if (params.flag) {
var nowX = e.clientX, nowY = e.clientY;
var disX = nowX - params.currentX, disY = nowY - params.currentY;
target.style.left = parseInt(params.left) + disX + "px";
target.style.top = parseInt(params.top) + disY + "px";
}
}
};
元素跟着动起来啦!
可以尝试把这里的 DEMO 和 drag 的方法合并使用,这里就不赘述啦。
上述为PC的拖动,我们把chrome的模拟器切到手机模式,oh myGod!!炫酷的效果全都失效了!!!!桑心~~~~
下面我们来看下移动端的拖拽操作:
在这之前先对touch事件有一个了解:学习链接
有一些不得不说的概念:
-
screenX: 触摸点相对于屏幕左边缘的 x 坐标。
-
screenY: 触摸点相对于屏幕上边缘的 y 坐标。
-
clientX: 触摸点相对于浏览器的 viewport左边缘的 x 坐标。不会包括左边的滚动距离。
-
clientY: 触摸点相对于浏览器的 viewport上边缘的 y 坐标。不会包括上边的滚动距离。
-
pageX: 触摸点相对于 document的左边缘的 x 坐标。 与 clientX不同的是,他包括左边滚动的距离,如果有的话。
-
pageY: 触摸点相对于 document的左边缘的 y 坐标。 与 clientY不同的是,他包括上边滚动的距离,如果有的话。
-
target: 总是表示 手指最开始放在触摸设备上的触发点所在位置的 element。即使已经移出了元素甚至移出了document, 他表示的element仍然不变
掌握了上述概念打开控制台,让我来观察元素的运动吧!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style type="text/css">
#content {
width: 200px;
height: 200px;
background: #ff6632;
position: absolute;
left: 0;
top: 0;
}
</style>
</head>
<body>
<div id="content">test</div>
</body>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
(function (window) { //传入window,提高变量的查找效率
function myQuery(selector) { //这个函数就是对外提供的接口。
//调用这个函数的原型对象上的_init方法,并返回
return myQuery.prototype._init(selector);
}
myQuery.prototype = {
/*初始化方法,获取当前query对象的方法*/
_init: function (selector) {
if (typeof selector == "string") {
//把查找到的元素存入到这个原型对象上。
this.ele = window.document.querySelector(selector);
//返回值其实就是原型对象。
return this;
}
},
/*单击事件:
* 为了规避click的300ms的延迟,自定义一个单击事件
* 触发时间:
* 当抬起手指的时候触发
* 需要判断手指落下和手指抬起的事件间隔,如果小于500ms表示单击时间。
*
* 如果是大于等于500ms,算是长按时间
* */
tap: function (handler) {
this.ele.addEventListener("touchstart", touchFn);
this.ele.addEventListener("touchend", touchFn);
var startTime,
endTime;
function touchFn(e) {
e.preventDefault()
switch (e.type) {
case "touchstart":
startTime = new Date().getTime();
break;
case "touchend":
endTime = new Date().getTime();
if (endTime - startTime < 500) {
handler.call(this, e);
}
break;
}
}
},
/**
* 长按
* @param handler
*/
longTag: function (handler) {
this.ele.addEventListener("touchstart", touchFn);
this.ele.addEventListener("touchmove", touchFn);
this.ele.addEventListener("touchend", touchFn);
var timerId;
function touchFn(e) {
switch (e.type) {
case "touchstart": //500ms之后执行
timerId = setTimeout(function () {
handler.call(this, e);
}, 500)
break;
case "touchmove":
//如果中间有移动也清除定时器
clearTimeout(timerId)
break;
case "touchend":
//如果在500ms之内抬起了手指,则需要定时器
clearTimeout(timerId);
break;
}
}
},
/**
* 左侧滑动。
记录手指按下的左边,在离开的时候计算 deltaX是否满足左滑的条件
*
*/
slide: function (handler) {
this.ele.addEventListener("touchstart", touchFn);
this.ele.addEventListener("touchend", touchFn);
var startX, startY, endX, endY;
function touchFn(e) {
e.preventDefault();
var firstTouch = e.changedTouches[0];
switch (e.type) {
case "touchstart":
startX = firstTouch.pageX;
startY = firstTouch.pageY;
break;
case "touchend":
endX = firstTouch.pageX;
endY = firstTouch.pageY;
//x方向移动大于y方向的移动,并且x方向的移动大于25个像素,表示在向左侧滑动
if (Math.abs(endX - startX) >= Math.abs(endY - startY) && startX - endX >= 25) {
handler.call(this, e, 'left');
}else if(Math.abs(endX - startX) >= Math.abs(endY - startY) && startX - endX < -25){
handler.call(this, e, 'right');
}else if(Math.abs(endX - startX) < Math.abs(endY - startY) && startY - endY >= 25){
handler.call(this, e, 'up');
}else if(Math.abs(endX - startX) < Math.abs(endY - startY) && startY - endY < -25){
handler.call(this, e, 'down');
}
break;
}
}
},
/**
* 右侧滑动。
*
*/
slideRight: function (e) {
}
}
window.? = window.myQuery = myQuery;
})(window);
?("div").tap(function (e) {
console.log("单击事件")
})
?("div").slide(function (e, type) {
let content = ''
console.log(this);
type === 'left' && (content = "左侧滑动了.....");
type === 'right' && (content = "右侧滑动了....");
type === 'up' && (content = "向上滑动了....");
type === 'down' && (content = "向下滑动了....");
this.innerHTML = content;
})
?("div").longTag(function () {
console.log("长按事件");
})
var _x_start, _y_start, _x_move, _y_move, _x_end, _y_end, left_start, top_start;
document.getElementById("content").addEventListener("touchstart", function (e) {
_x_start = e.touches[0].pageX;
_y_start = e.touches[0].pageY;
left_start = $("#content").css("left");
top_start = $("#content").css("top");
})
document.getElementById("content").addEventListener("touchmove", function (e) {
_x_move = e.touches[0].pageX;
_y_move = e.touches[0].pageY;
$("#content").css("left", parseFloat(_x_move) - parseFloat(_x_start) + parseFloat(left_start) + "px");
$("#content").css("top", parseFloat(_y_move) - parseFloat(_y_start) + parseFloat(top_start) + "px");
})
document.getElementById("content").addEventListener("touchend", function (e) {
// var _x_end=e.changedTouches[0].pageX;
// var _y_end=e.changedTouches[0].pageY;
// console.log("end",_x_end)
})
//阻止浏览器下拉事件
$('body').on('touchmove', function (event) { event.preventDefault(); });
</script>
</html>
是不是觉得可以很自由的操控一个元素了?哈哈哈。。。
接下来我们在看一个进度条的例子:
这里有一点对range的扩展,有兴趣的可以了解一下,不看也不影响理解。--学习链接
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style type="text/css">
.scroll {
top: 100px;
left: 100px;
position: relative;
width: 500px;
height: 5px;
background: rgb(180, 180, 180);
}
.bar {
position: absolute;
top: -3px;
height: 10px;
width: 8px;
background: rgb(255, 0, 0);
}
.mask {
position: absolute;
height: 5px;
background: rgb(255, 0, 0);
}
</style>
</head>
<body>
<div class="scroll">
<div class="bar"></div>
<div class="mask"></div>
</div>
<p></p>
</body>
<script>
// debugger
var bar = document.getElementsByClassName('bar')[0];
var scroll = document.getElementsByClassName('scroll')[0];
var bar = document.getElementsByClassName('bar')[0];
var mask = document.getElementsByClassName('mask')[0];
bar.onmousedown = function(e){
var leftVal = e.clientX - this.offsetLeft;
var that = this;
document.onmousemove = function(e){
barLeft = e.clientX - leftVal;
if(barLeft < 0){
barLeft = 0
}else if(barLeft > scroll.offsetWidth - bar.offsetWidth){
barLeft = scroll.offsetWidth - bar.offsetWidth;
}
bar.style.left = barLeft + 'px';
mask.style.width = barLeft + 'px';
document.getElementsByTagName('p')[0].innerHTML = '已经完成了' + Math.floor((barLeft / (scroll.offsetWidth - bar.offsetWidth))*100) + '%'
//防止拖动过快时鼠标弹起后mousemove还在生效
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
}
}
document.onmouseup = function () {
document.onmousemove = null; //弹起鼠标不做任何操作
}
</script>
</html>
是不是感觉还挺实用的,哈哈哈!
好啦,今天先扯到这里。
end。。。。。