js 实现 移动端 拖拽效果
点击下面 目录标题 在线预览效果
网页 head 部分
<!-- 禁止缩放 --> <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
公共 js 部分
//提取非行间样式 function getStyle(obj,attr) { if(obj.currentStyle) { return obj.currentStyle[attr];} else { return getComputedStyle(obj,false)[attr];} }
addEventListener 函数中 {passive: false} 参数说明:
Passive Event Listeners就是告诉前页面内的事件监听器内部是否会调用preventDefault函数来阻止事件的默认行为,以便浏览器根据这个信息更好地做出决策来优化页面性能。当属性passive的值为true的时候,代表函数(该监听器)内部不会调用 preventDefault 函数来阻止默认滑动行为;passive 的值为 false ,代表函数内部调用 preventDefault 函数来阻止默认滑动行为,代表函数内部调用 ,Chrome浏览器称这类型的监听器为被动(passive)监听器。目前Chrome主要利用该特性来优化页面的滑动性能,所以Passive Event Listeners特性当前仅支持 mousewheel/touch 相关事件。
手机端这里 touchStartEvent 函数中的对象使用 obj , 没有拖拽过快,obj 跟不上的问题。
移动端单个退拽元素
css 部分
body{height:2000px;} .test{margin:150px; } #ele{width:150px; height:150px;cursor: move;position: fixed;top:50px;color:#fff;box-sizing: border-box;z-index: 10;background:#2196f3;left:10px; font-size: 16px}
js 部分
window.onload=function(ev){ // 获取元素 var Div=document.getElementById('ele'); // 开始拖拽 drag(Div,{passive: false}); }
1 // 拖拽运动 2 function drag(obj,passive){ 3 4 var lastX=0; 5 var lastY=0; 6 7 obj.addEventListener('touchstart',touchStartEvent,passive); 8 // ev 为系统自带参数 9 function touchStartEvent(ev) 10 { 11 var Ev=ev||event; // FF || IE 12 var touch = Ev.targetTouches[0]; 13 var disX=touch.clientX-parseInt(getStyle(obj,'left')); 14 var disY=touch.clientY-parseInt(getStyle(obj,'top')); 15 16 //释放捕获,把鼠标事件 捕获到 当前文档指定的对象 针对IE 17 if(obj.setCapture) 18 { 19 obj.addEventListener('touchmove',touchMoveEvent,passive); 20 obj.addEventListener('touchend',touchEndEvent,passive); 21 obj.setCapture(); 22 } 23 else 24 { 25 // 手机端这里使用obj ,没有拖拽过快,obj 跟不上的问题 26 obj.addEventListener('touchmove',touchMoveEvent,passive); 27 obj.addEventListener('touchend',touchEndEvent,passive); 28 } 29 // 鼠标移动,函数上的 ev 参数是系统传过来的 30 function touchMoveEvent(ev) 31 { 32 var Ev=ev||event; // FF || IE 33 var touch = Ev.targetTouches[0]; 34 var l=touch.clientX-disX; 35 var t=touch.clientY-disY; 36 37 38 if(l>=document.documentElement.clientWidth-parseInt(getStyle(obj,'width'))) 39 { 40 l=document.documentElement.clientWidth-parseInt(getStyle(obj,'width')); 41 } 42 else if(l<=0) 43 { l=0; } 44 45 if(t>=document.documentElement.clientHeight-parseInt(getStyle(obj,'height'))) 46 { 47 t=document.documentElement.clientHeight-parseInt(getStyle(obj,'height')); 48 } 49 else if(t<=0) 50 { t=0; } 51 52 lastX=l; 53 lastY=t; // 当前的点赋给一个变量(上一个点) 54 obj.style.left=l+'px'; 55 obj.style.top=t+'px'; //移动物体 56 } 57 // 鼠标抬起 58 function touchEndEvent() 59 { 60 obj.removeEventListener('touchmove',touchMoveEvent,passive); 61 obj.removeEventListener('touchend',touchEndEvent,passive); 62 if(obj.releaseCapture) 63 { obj.releaseCapture(); } 64 65 } 66 // 拖拽元素时,阻止页面跟随滚动问题 67 // 由于 {passive: false} ,如果不阻止默认事件,chrome 浏览器会有警告 68 Ev.preventDefault(); 69 return false; 70 } 71 } 72 73 //提取非行间样式 74 function getStyle(obj,attr) 75 { 76 if(obj.currentStyle) 77 { return obj.currentStyle[attr];} 78 else 79 { return getComputedStyle(obj,false)[attr];} 80 }
html 部分
<div id="ele">单个退拽元素<br/>原生js<br/>不跟随页面滚动</div> <p class="test">测试内容区域1</p> <p class="test">测试内容区域2</p> <p class="test">测试内容区域3</p>
移动端多个拖拽元素
css 部分
body{height:1500px;} *{margin:0px;padding:0px;} .test{margin:200px; } #ele1,#ele2,#ele3{width:100px; height:100px;cursor: move;position: fixed;top:50px;color:#fff;box-sizing: border-box;z-index: 10;font-size: 14px} #ele1{background:#2196f3;top:40px; } #ele2{background:#ff5722;top:160px;} #ele3{background:#08c80f;top:270px; }
js 部分
window.onload=function(){ //获取拖拽的元素 var ele1=document.getElementById('ele1'); var ele2=document.getElementById('ele2'); var ele3=document.getElementById('ele3'); // 层级问题默认 ele1:1,ele2:2,ele3:3 // 定义公共层级变量,3个定义的一样,这里取了第一个 this.layer=parseInt(getStyle(ele1,'z-index')) // 调用拖拽函数 drag(ele1,{passive: false}); drag(ele2,{passive: false}); drag(ele3,{passive: false}); }
1 // 拖拽运动 2 function drag(obj,passive){ 3 4 var lastX=0; 5 var lastY=0; 6 7 obj.addEventListener('touchstart',touchStartEvent,passive); 8 9 function touchStartEvent(ev) 10 { 11 var Ev=ev||event; // FF || IE 12 var touch = Ev.targetTouches[0]; 13 var disX=touch.clientX-parseInt(getStyle(obj,'left')); 14 var disY=touch.clientY-parseInt(getStyle(obj,'top')); 15 // 层级问题,最后拖拽的在最上面,由于 zIndex 最大值是一个非常大的数,所以这里没有限制处理 16 obj.style.zIndex=window.layer=window.layer+1 17 18 //释放捕获,把鼠标事件 捕获到 当前文档指定的对象 针对IE 19 if(obj.setCapture) 20 { 21 obj.addEventListener('touchmove',touchMoveEvent,passive); 22 obj.addEventListener('touchend',touchEndEvent,passive); 23 obj.setCapture(); 24 } 25 else 26 { 27 obj.addEventListener('touchmove',touchMoveEvent,passive); 28 obj.addEventListener('touchend',touchEndEvent,passive); 29 } 30 // 鼠标移动,函数上的 ev 参数是系统传过来的 31 function touchMoveEvent(ev) 32 { 33 var Ev=ev||event; // FF || IE 34 var touch = Ev.targetTouches[0]; 35 var l=touch.clientX-disX; 36 var t=touch.clientY-disY; 37 38 if(l>=document.documentElement.clientWidth-parseInt(getStyle(obj,'width'))) 39 { 40 l=document.documentElement.clientWidth-parseInt(getStyle(obj,'width')); 41 } 42 else if(l<=0) 43 { l=0; } 44 45 if(t>=document.documentElement.clientHeight-parseInt(getStyle(obj,'height'))) 46 { 47 t=document.documentElement.clientHeight-parseInt(getStyle(obj,'height')); 48 } 49 else if(t<=0) 50 { t=0; } 51 52 lastX=l; 53 lastY=t; // 当前的点赋给一个变量(上一个点) 54 obj.style.left=l+'px'; 55 obj.style.top=t+'px'; //移动物体 56 } 57 // 鼠标抬起 58 function touchEndEvent() 59 { 60 obj.removeEventListener('touchmove',touchMoveEvent,passive); 61 obj.removeEventListener('touchend',touchEndEvent,passive); 62 if(obj.releaseCapture) 63 { obj.releaseCapture(); } 64 65 } 66 // 拖拽元素时,阻止页面跟随滚动问题 67 // 由于 {passive: false} ,如果不阻止默认事件,chrome 浏览器会有警告 68 Ev.preventDefault() 69 return false; 70 } 71 }
html 部分
<div id="ele1">多个退拽元素<br/>原生js<br/>不跟随页面滚动<br/>最后拖拽的在最上面</div> <div id="ele2">多个退拽元素<br/>原生js<br/>不跟随页面滚动<br/>最后拖拽的在最上面</div> <div id="ele3">多个退拽元素<br/>原生js<br/>不跟随页面滚动<br/>最后拖拽的在最上面</div> <p class="test">测试内容区域</p> <p class="test">测试内容区域2</p> <p class="test">测试内容区域3</p>
单个退拽元素添加配置项
css 部分
/*body{height:1000px;}*/ *{margin:0px;padding:0px;} .test{margin:150px; } /*如果父元素不是body,建议手动指定 left top 值,如果不指定 默认 bottom:0; left:50( 我这里设置margin:50 原因); */ #warp{width:260px;height:260px;border: 1px #ccc solid;margin:50px; background: #fff;left:-20px;top:50px;z-index: 9;} /*拖拽范围元素 层级要低于 拖拽元素层级*/ #ele{width:150px; height:150px;cursor: move;color:#fff;box-sizing: border-box;z-index: 10;font-size: 16px;background:#2196f3; }
js 部分
配置参数:
top,bottom,left,right 默认值为 0 ,拖拽元素 距离 范围元素(默认body) 的距离
direction 拖拽方向,默认为任意方向,lr(左右) tb(上下)
parent:'warp', 为页面唯一元素,拖拽元素活动范围 元素,id 名称,例如 warp ,默认body整个页面
scroll:false 拖拽元素是否随 页面滚动 false 不随页面滚动,true 随页面滚动
// 当页面加载的时候----如果添加 onresize ,页面滚动,拖拽元素恢复初始值 window.onload=function(){ // 配置选项 var opt={ top:0, bottom:0, left:20, right:10, parent:'warp', // 为页面唯一元素,id 名称,例如 warp ,默认body整个页面 scroll:true // 拖拽元素是否随 页面滚动 false 不随页面滚动,true 随页面滚动 } var obj=document.getElementById('ele'); // 调用拖拽函数 drag(obj,opt,{passive: false}); }
1 // 拖拽运动 2 function drag(obj,opt={},passive){ 3 // 默认配置选项 4 var option={ 5 direction:'auto',//方向 lr(左右) tb(上下) 6 // 运动范围 默认父级边界 7 top:0, 8 bottom:0, 9 left:0, 10 right:0, 11 //parent:'warp', // 为页面唯一元素,id 名称,例如 warp ,默认body整个页面 12 scroll:false // 拖拽元素是否随 页面滚动 false 不随页面滚动,true 随页面滚动 13 } 14 // 合并对象参数 15 Object.assign(option,opt) 16 17 var lastX=0; 18 var lastY=0; 19 var parent_width=0,parent_height=0,parent_left=0,parent_top=0; 20 var ele_position='fixed' 21 if(option.scroll){ 22 ele_position='absolute' 23 }else{ 24 // 如果不跟随页面滚动,并且元素父级不是body,父元素定位, 25 if(option.parent) 26 document.getElementById(option.parent).style.position='fixed' 27 } 28 29 // 设置元素的定位,需要放置获取 parent_* 值之前 30 obj.style.position=ele_position 31 32 if(option.parent){ 33 var parent_ele=document.getElementById(option.parent) 34 parent_width=parent_ele.clientWidth; 35 parent_height=parent_ele.clientHeight; 36 parent_left=parent_ele.offsetLeft; 37 parent_top=parent_ele.offsetTop; 38 }else{ 39 if(option.scroll){ 40 parent_width=document.documentElement.scrollWidth 41 parent_height=document.documentElement.scrollHeight 42 }else{ 43 parent_width=document.documentElement.clientWidth 44 parent_height=document.documentElement.clientHeight 45 } 46 } 47 48 // 设置元素位置 49 obj.style.left=option.left+parent_left+'px' 50 obj.style.top=option.top+parent_top+'px' 51 52 // 手指按下 53 obj.addEventListener('touchstart',touchStartEvent,passive); 54 55 function touchStartEvent(ev) 56 { 57 var Ev=ev||event; // FF || IE 58 var touch = Ev.targetTouches[0]; 59 var disX=0,disY=0; 60 61 if(option.direction=="lr"){ 62 disX=touch.clientX-parseInt(getStyle(obj,'left')); 63 }else if(option.direction=="tb"){ 64 disY=touch.clientY-parseInt(getStyle(obj,'top')); 65 }else{ 66 disX=touch.clientX-parseInt(getStyle(obj,'left')); 67 disY=touch.clientY-parseInt(getStyle(obj,'top')); 68 } 69 70 if(obj.setCapture)//设置事件捕获 针对IE 71 { 72 obj.addEventListener('touchmove',touchMoveEvent,passive); 73 obj.addEventListener('touchend',touchEndEvent,passive); 74 obj.setCapture(); 75 }else 76 { 77 obj.addEventListener('touchmove',touchMoveEvent,passive); 78 obj.addEventListener('touchend',touchEndEvent,passive); 79 } 80 // 鼠标移动 81 function touchMoveEvent(ev) 82 { 83 var Ev=ev||event; // FF || IE 84 var touch = Ev.targetTouches[0]; 85 86 var left=0,top=0,move_left=0,move_top=0 87 if(option.direction=="lr"){ 88 // 可移动的最大左边距离 89 move_left=parent_width-parseInt(getStyle(obj,'width'))-option.right+parent_left 90 if(left>=move_left){ 91 left=move_left 92 }else if(left<=option.left+parent_left){ 93 left=option.left+parent_left 94 }else{ 95 left=touch.clientX-disX; 96 } 97 lastX=left; 98 obj.style.left=left+'px'; 99 100 }else if(option.direction=="tb"){ 101 // 可以移动的最大上边距 102 move_top=parent_height-parseInt(getStyle(obj,'height'))-option.top+parent_top 103 if(top>=move_top){ 104 top=move_top 105 }else if(top<=option.bottom+parent_top){ 106 top=option.bottom 107 }else{ 108 top=touch.clientY-disY; 109 } 110 lastY=top; 111 obj.style.top=top+'px'; 112 }else{ 113 left=touch.clientX-disX; 114 top=touch.clientY-disY; 115 116 move_left=parent_width-parseInt(getStyle(obj,'width'))-option.right+parent_left 117 move_top=parent_height-parseInt(getStyle(obj,'height'))-option.bottom+parent_top 118 119 if(left>=move_left){ 120 left=move_left 121 }else if(left<=option.left+parent_left){ 122 left=option.left+parent_left 123 } 124 if(top>=move_top){ 125 top=move_top 126 }else if(top<=option.bottom+parent_top){ 127 top=option.bottom+parent_top 128 } 129 130 lastX=left; 131 lastY=top; // 当前的点赋给一个变量(上一个点) 132 obj.style.left=left+'px'; 133 obj.style.top=top+'px'; //移动物体 134 } 135 } 136 // 鼠标抬起 137 function touchEndEvent() 138 { 139 obj.addEventListener('touchmove',touchMoveEvent,passive); 140 obj.addEventListener('touchend',touchEndEvent,passive); 141 //释放事件捕获 142 if(obj.releaseCapture) 143 { obj.releaseCapture(); } 144 145 } 146 // 拖拽元素时,阻止页面跟随滚动问题 147 // 由于 {passive: false} ,如果不阻止默认事件,chrome 浏览器会有警告 148 Ev.preventDefault() 149 return false; 150 } 151 }
html 部分
<p>指定元素内拖拽,居左20px,居右10px</p> <p>随页面滚动</p> <div id="warp">指定元素内拖拽</div> <p class="test">测试内容区域</p> <div id="ele">单个拖拽元素<br/>原生js<br/>添加配置选项</div> <p>测试部分</p> <p class="test">测试内容区域2</p>