0

0

javascript实现PC网页里的拖拽效果_javascript技巧

php中文网

php中文网

发布时间:2016-05-16 15:10:55

|

1718人浏览过

|

来源于php中文网

原创

几年前,我参与设计开发一个房产网的项目,我负责前端工作,由于项目经理要求比较高,参考了很多房产类网站比较优秀的功能,想把别人比较优秀的设计和想法集合到一起,那时的设计稿和功能实现,简直就是改了又改,今天做好的一个很好的效果,可能第二天就要推到重来,算了,不说这些了,还是说说我们今天要讲解的案例吧,不知道大家访问过搜房网没有(完全没有做广告之嫌,搜房网,可以给点广告费不),其中有一个功能产品经理特别喜欢,那,就是下面的这个:

这是现在的效果,可能改了一些,原来的效果是,里面的这张图是可以上下左右拖动的,然后房子上面的显示的楼栋号,也跟着图片一起移动,当时js能力还不行,未能实现项目经理的要求,不过后来项目经理又把这个效果推掉了,换了另外的一个效果

尽管项目经理不想要这个效果了,但是当时就在我心里留下了一个节,到今天都忘不了这个梗。

好了,这就是我今天想写这篇博客的初衷,希望能给想实现这类拖拽效果,但是不知道该怎么去实现的同学,提供一种思路,不给青春留遗憾,当然实现拖拽的方法有很多,这里就只介绍JavaScript中的一种方法,慢慢体会一下其中的原理!

好了,梗也说完了,开始正题,我们先要明白,拖拽到底是一个什么东西,你也知道,我也知道,但是我还是想来描述一下:

立即学习Java免费学习笔记(深入)”;

拖拽就是一个容器,你用鼠标可以在页面上拖着到处跑,废话,精确的描述应该是,鼠标移到容器上,然后鼠标按下去,注意要按着不放,然后拖动鼠标,容器能跟着鼠标跑,松开鼠标,容器就停在那里不动了,现实中的例子就是桌子上有一个盒子,我用手放在盒子上,然后移动盒子,手停盒子停,手拿开,盒子不动了,嘻嘻,都懂了哈!

别以为上面说了一堆的废话,我们可以从中得到很多的信息,总结如下就是:

拖拽 = 鼠标按下 + 鼠标移动 + 鼠标弹上

这样就完成了一个拖拽任务,好了,原来这就是拖拽的原理,想实现拖拽,自然实现上面的3个动作,便可以模拟拖拽效果,好,对应JavaScript中的语法就是需要实现这3个动作:

onmousedown , onmousemove , onmouseup

 实现的代码就应该是:

obj.onmousedown = function(ev){
   obj.onmousemove = function(ev){
 
   } ;
   obj.onmouseup = function(ev){
   
   };
   
}

为什么后面2个动作要写的里面,好好回味一下,好了,第一步的大概思路就有了,下一步就需要考虑怎么让物体跟着鼠标一起移动,思路大概是这样的:

首先物体是需要决定定位的,因为我们需要操作它的left和top值,才能让它移动,然后就是要考虑鼠标了,鼠标位移,本身就会有一个距离,如果我们知道鼠标移动了多远,然后把这个距离给物体,那物体是不是也和鼠标一样,移动了相同的距离,这不就实现拖拽了吗?哈哈,思路一点点有,感觉萌萌哒~ 现在的问题就是怎么获取鼠标的距离,如果需要深入了解,请复习一下盒子模型,这里我就不说了,很多大神也有相关的博客,我用一张图表示一下:

说明:蓝色框为屏幕宽高,黑色粗框为浏览器可视区宽高(浏览器缩小效果),黑色细框为鼠标要拖拽的对象,如图可知,获取鼠标的坐标,可以用event.clientX,event.clientY来获取,哦了;

计算的大致原理可以参照下图:

说明:左边为初始位置,右边为目标位置,原点为鼠标位置,大黑框为浏览器可视宽度,小黑框为拖拽对象,看拖拽对象到目标位置的状态,获取鼠标的最终位置,再减去鼠标距离对象的差值,再赋值给对象的top,left值,也可以获取鼠标的位置差值,再用初始的top,left值加上差值,我们采用第一种,第二种也可以,自己去试一下:

 obj.onmousedown = function(ev){
  var ev = ev || event;
  var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop;
 
  document.onmousemove = function(ev){
    var ev = ev || event;
    obj.style.left = ev.clientX - disX + 'px';
    obj.style.top = ev.clientY - disY + 'px';
  };
  document.onmouseup = function(ev){
    var ev = ev || event;
    document.onmousemove = document.onmouseup = null;
  };
}

这里说明一下:onmousemove和onmouseup之所以用document对象而不用obj对象,是因为如果用obj对象,鼠标在obj内部还好,如果在obj外面的话,拖拽会很怪异,你也可以改成obj体会一下,最后我们在鼠标弹起的时候将事件都清空;

绘影字幕
绘影字幕

视频字幕制作神器、轻松编辑影片

下载

上面的基本拖拽就算完成了,但是细心的同学一定会问,如果页面上有文字的话,拖拽物体会将文字选中,这效果岂不是怪怪的,没错,这是因为拖拽的时候触发了浏览器的默认选择事件,所以,在拖拽的时候,我们要清除这个默认事件,那怎么清除呢?

下面给一个兼容性写法:

if(ev.stopPropagation){
   ev.stopPropagation();
}else{
  ev.cancelBubble = true; //兼容IE
}
//简写成
ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true;

将上面的代码放在onmousedown下,鼠标按下就清除浏览器默认事件,文字就不会被选中了,好了,一个简单的拖拽效果就完成了,当然你现在是看不到效果,之所以不给demo链接是为了让你自己试着写一写,这样印象更深刻,

好了,那问题又来了,到这里就这样完了吗?。。。。。。按本人的风格,当然没有,干货还在后面!

如果我想实现这样一个效果,就是这一个大的容器里面(可以是box,也可以是document),怎么样能让我们的拖拽对象不跑出去呢,换句话说,拖到边缘就拖不动了,耶,是不是很多人想要实现的效果,哈哈,我们看看实现的原理是什么:

现实生活中,一个物体在一个盒子里跑不出去,是因为有堵墙,那我们只要能模拟出这堵墙,就可以把物体框起来,那这堵墙要怎么做呢?我们可以换个思路,当拖拽对象拖到边缘的时候,比如说拖到右边,我们将它的left固定住,是不是就不能再往右了,因为left值不能再加了,那么拖到底部,同理我们将top值固定住,就不能再往下拖了,理解吗?

最终的结果就是如下:

//左侧
if(obj.offsetLeft <=0){
  obj.style.left = 0;
};
//右侧
if(obj.offsetLeft >= pWidth - oWidth){
  obj.style.left = pWidth - oWidth + 'px'; 
};
//上面
if(obj.offsetTop <= 0){
  obj.style.top = 0; 
};
//下面
if(obj.offsetTop >= pHeight - oHeight){
  obj.style.top = pHeight - oHeight + 'px'; 
};

说明:pWidth,pHeight 表示父级元素的宽高(这里是表示相对于父级的宽高限制),oWidth,oHeigt表示拖拽元素的宽高

最后,我将整个拖拽代码整理了一下:

/*
      参数说明:
      元素绝对定位,父级相对定位,如果父级为window,则可以不用
      传一个参数,表示父级为window,物体相对于window范围拖动
      传2个参数,则父级为第二个参数,物体相对于父级范围拖动
      参数为id值
    */
    function drag(obj,parentNode){
      var obj = document.getElementById(obj);
      if(arguments.length == 1){
        var parentNode = window.self; 
        var pWidth = parentNode.innerWidth,pHeight = parentNode.innerHeight;  
      }else{
        var parentNode = document.getElementById(parentNode);
        var pWidth = parentNode.offsetWidth,pHeight = parentNode.offsetHeight;
      }
      obj.onmousedown = function(ev){
        var ev = ev || event;
        var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop;
        var oWidth = obj.offsetWidth,oHeight = obj.offsetHeight;
         
        //阻止冒泡时间
        ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true;
       
         
        document.onmousemove = function(ev){
          var ev = ev || event;
          obj.style.left = ev.clientX - disX + 'px';
          obj.style.top = ev.clientY - disY + 'px';
           
          //左侧
          if(obj.offsetLeft <=0){
            obj.style.left = 0;
          };
          //右侧
          if(obj.offsetLeft >= pWidth - oWidth){
            obj.style.left = pWidth - oWidth + 'px'; 
          };
          //上面
          if(obj.offsetTop <= 0){
            obj.style.top = 0; 
          };
          //下面
          if(obj.offsetTop >= pHeight - oHeight){
            obj.style.top = pHeight - oHeight + 'px'; 
          };
        };
        document.onmouseup = function(ev){
          var ev = ev || event;
          document.onmousemove = document.onmouseup = null;
        };
      }
         
    }

说明:我这里处理的效果是,如果传一个参数,表示相对的对象是window对象,如果传2个参数,第一个是拖拽对象,第二个为相对父级

开篇就说了,搜房网的那个图片拖拽效果是我的一个心结,我写了一个类似的效果,供大家参考,因为自己没有买服务器,所以效果我就不展示了,直接把代码贴出来,供大家参考:

css:

<style>
.box{
  width:600px;
  height:400px;
  margin:50px auto;
  position:relative;
  overflow:hidden;
}
#box{
  width:1000px;
  height:800px;
  position:absolute;
  left:50%;
  top:50%;
  margin:-400px 0 0 -500px;
}
#pic{ width:800px; height:600px; background:url(images/pic1.jpg) no-repeat; position:absolute; left:100px; top:100px; }
#pic:hover{
  cursor:move;
}
</style>

html:

<div class="box">
    <div id="box">
      <div id="pic"></div>
    </div>
  </div>

javascript:

window.onload = function(){
     
    drag("pic","box");
    function drag(obj,parentNode){
      var obj = document.getElementById(obj);
      if(arguments.length == 1){
        var parentNode = window.self; 
        var pWidth = parentNode.innerWidth,pHeight = parentNode.innerHeight;  
      }else{
        var parentNode = document.getElementById(parentNode);
        var pWidth = parentNode.offsetWidth,pHeight = parentNode.offsetHeight;
      }
      obj.onmousedown = function(ev){
        var ev = ev || event;
        var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop;
        var oWidth = obj.offsetWidth,oHeight = obj.offsetHeight;
         
        //阻止冒泡时间
        ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true;
       
         
        document.onmousemove = function(ev){
          var ev = ev || event;
          obj.style.left = ev.clientX - disX + 'px';
          obj.style.top = ev.clientY - disY + 'px';
           
          //左侧
          if(obj.offsetLeft <=0){
            obj.style.left = 0;
          };
          //右侧
          if(obj.offsetLeft >= pWidth - oWidth){
            obj.style.left = pWidth - oWidth + 'px'; 
          };
          //上面
          if(obj.offsetTop <= 0){
            obj.style.top = 0; 
          };
          //下面
          if(obj.offsetTop >= pHeight - oHeight){
            obj.style.top = pHeight - oHeight + 'px'; 
          };
        };
        document.onmouseup = function(ev){
          var ev = ev || event;
          document.onmousemove = document.onmouseup = null;
        };
      }
         
    }
     
     
  }

效果完全是用的那个封装代码块,引用起来也挺方便,有人会问了,你这用的id获取DOM元素,一个页面只能用一次啊,如果页面多次使用呢,有道理,解决方案之一,那就命名不同的id呗,又不犯法,方案二,获取id的地方改成获取class,但是要注意的是,getElementsByClassName是获取的class集合,需要改写一下,这里我就不写了,有兴趣的同学自行改写一下,好了,到这里真的结束了!

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

705

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

233

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

117

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

22

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

61

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

30

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

15

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

669

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

58

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 5.3万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 3万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号