实现可拖拽,拉伸,吸附功能的甘特图(时间/任务表)
最近应为业务需求需要开发一个任务调度后台,实现一个甘特图( 类似上学时候的课程表,‘时间/课程/代课老师’ 转换为: “时间/任务/执行人'”)。参考图片:
每一行的00:00到24:00部分的 <div class="tr-right draggable ui-widget-content"> 是展示一个用户所有任务的容器;
每个一个粉色 <div class="dragItem" > 是一个 任务元素 ;
每个任务元素 <div class="rightLine"> 是此任务用来左右拉伸的长度控制游标;
<div class="tr-right draggable ui-widget-content"><div class="dragItem" style="left: 0px;"><div class="dragBox">任务三<div class="rightLine"></div></div></div><div class="dragItem" style="left: 100px;"><div class="dragBox">任务四<div class="rightLine"></div></div></div><div class="dragItem" style="left: 200px;"><div class="dragBox">任务五<div class="rightLine"></div></div></div><div class="dragItem" id="ddd" style="left: 300px;"><div class="dragBox">任务六<div class="rightLine"></div></div></div> </div>
首先,使用jqueryUI的 Draggable拖拽组件为每个任务元素绑定拖拽事件:
$(".dragItem").draggable({containment: "parent",axis: "x",cursor: "move",start: function(data,data1) {//元素拖拽开始 },drag: function(data,data1) {//元素拖拽中 },stop: function(data,data1) {//元素拖拽结束 } });
containment: "parent", //元素只能在其父容器内进行拖拽;
axis: "x", //元素只能沿着x轴做水平运动;
cursor: "move", //鼠标hover样式为move
此时任务元素已经可以拖动了,可是发现元素们再拖拽过程中可以相互重叠(因为拖拽只会改变元素的position:absolute的left值);
但是需求是:用户同一个时段只能进行一个任务;那么必然我们要做重叠判断,并且要做好堆叠情况下元素位置的reset( 吸附或者还原操作 )。
那么问题来了:
1. 怎么判定两个元素之间的位置关系?
2.判定两个元素是堆叠关系后,如何reset?
对于两个position: absolute的元素 A和 B,因为元素只能水平拖拽,所以他们的top: 0 ;他们两个的位置关系有一下几种:
1. A被 B包含。
2. A包含了 B。
3. A的右边和 B有重叠。
4. A的左边和 B有重叠。
5. A与 B没有重叠部分。
function situation(a,b){ var aLeft = a.position().left; //a元素的left值var aRight = aLeft + a.outerWidth(); //a元素right。。。var bLeft = b.position().left;var bRight = bLeft + b.outerWidth();//a元素中心距离b元素中心的距离 var l_r = ((bRight+bLeft)-(aRight+aLeft))/2;if(aLeft>=bLeft&&aRight<=bRight){//a被b包含return [0,l_r];}if(bLeft>=aLeft&&bRight<=aRight){//a包含了breturn [1,l_r];}if(((bLeft>=aLeft&&bLeft<aRight)&&(bRight>aRight))||((aRight>bLeft&&aRight<=bRight)&&(aLeft<bLeft))){//a的右边和b有重叠return [2,l_r];}if(((bRight>aLeft&&bRight<=aRight)&&(bLeft<aLeft))||((aLeft>=bLeft&&aLeft<bRight)&&(aRight>bRight))){//a的左边和b有重叠return [3,l_r];}//a和b没有任何重叠return [4,l_r]; } //此方法分别输入a,b元素的left,right值,结果返回true代表a,b有重合部分,返回false代表啊,b辆元素不重合。
function checkPoint(a, b, c, d) {
//a代表a元素的left值,b代表a元素的right值,c代表b元素的left值,d代表b元素的right值。
return(a >= c && b <= d) ||
(c >= a && d <= b) ||
(((c >= a && c < b) && (d > b)) || ((b > c && b <= d) && (a < c))) ||
(((d > a && d <= b) && (c < a)) || ((a >= c && a < d) && (b > d)));
}
situation(a,b)方法返回a,b两个元素的位置关系:
1. A被 B包含。[ 0, l_r ]
2. A包含了 B。[ 1, l_r ]
3. A的右边和 B有重叠。[ 2, l_r ]
4. A的左边和 B有重叠。[ 3, l_r ]
5. A与 B没有重叠部分。[ 4, l_r ]
l_r>0代表B元素中心在A元素中心的右边。反之在左边。
接着分析需求:
拖拽两个任务对象有重叠时,需要对拖拽对象与其重叠对象做吸附处理。
就是说:在拖拽并释放元素A的瞬间,需要判定A是否与其他任务元素有重叠。
1. 如果此时有元素B与A重叠,需要将A吸附到元素B的一边,至于吸附到B的左边还是右边,非常简单:判断释放时A的中心点在B的左边还是右边,
左边就将A的最右侧吸附到B的最左侧,右边就将A的最左侧吸附到B的最右侧。
2. 如果A释放的瞬间,有B,C,D...与A有重叠,需要A吸附到那个与他中心点距离最近的重叠元素。
3. 如果A吸附到B元素后,会与C,D,E...重合。则重置A到拖拽之前的初始位置。
4. 如果A吸附到B元素后,A元素却超出了父元素内部分。则重置A到拖拽之前的初始位置。
//拖拽 $(".dragItem").draggable({containment: "parent",axis: "x",cursor: "move",stop: function(data, data1) {//拖拽任务对象var zj = data1.helper;//获取与自己中心最近的接触对象var mostTouchDom = getMostTouchDom(zj);if(mostTouchDom != false) { //释放后是会和其他元素产生堆叠if(mostTouchDom.touchRange >= 0) { //如果touchRange>0,自己中心在接触元素中心的左边if(!eachItemsLR(0, zj[0], mostTouchDom.dom, 0, ($(mostTouchDom.dom).position().left - zj.outerWidth()))) { //如果吸附以后,不会再与其他元素产生重叠,此时执行吸附操作:zj.css('left', ($(mostTouchDom.dom).position().left - zj.outerWidth()));} else { //吸附后会与其他元素产生堆叠,重置自己到拖拽前的初始状态zj.css('left', data1.originalPosition.left);}} else { //如果touchRange<0,自己的中心在接触元素中心的右边if(!eachItemsLR(0, zj[0], mostTouchDom.dom, 1, ($(mostTouchDom.dom).position().left +$(mostTouchDom.dom).outerWidth() + zj.outerWidth()), zj.parent())) { //同上个对称的ifzj.css('left', ($(mostTouchDom.dom).position().left + $(mostTouchDom.dom).outerWidth()));} else { //同上个对称elsezj.css('left', data1.originalPosition.left);}}}} });
//判断是否有兄弟元素与自己产生堆叠,如果有返回此兄弟元素,如果没有返回false; function getMostTouchDom(zj) {var choseList = [];zj.parent().find('.dragItem').each(function() {if(zj[0] != this) {var result = situation(zj, $(this));if(result[0] < 4 && result[0] >= 0) {choseList.push({touchAbs: Math.abs(result[1]), //与重叠元素中心点的距离的绝对值dom: this, //此重叠元素对象touchRange: result[1] //与重叠元素中心点距离});}}});if(choseList.length == 0) {return false;} //数组以绝对值大小降序排列choseList.sort(function(a, b) {return a.touchAbs - b.touchAbs;}); //返回与zj中心点距离最近的元素return choseList[0]; }/* * //遍历检查:除了自身身和接触对象外,是否有其他元素会在吸附后产生重叠。
* type:0:为拖拽,1:为拉伸(拉伸稍后会介绍)* item:自身元素对象.* target:当前与自身元素接触目标元素对象.* lr:值为0:自身吸附在目标元素的左侧. 值为1:自身吸附在目标元素的右侧.* can:代表在假设吸附状态形成后,自身元素的left值.(左吸附)。自身元素的right值.(右吸附)。* box:当前任务的父元素(任务容器对象)。*/ function eachItemsLR(type, item, target, lr, can, box) {var bool = false;var boxRight = (typeof(box) == "undefined") ? false : box.width();$(item).parent().find('.dragItem').each(function() {if(item != this && target != this) {if(lr == 0) {//将要左吸附,对假设已完成左吸附的元素做容器类的重叠检测 //type==0时为拖拽功能逻辑:checkPoint()第一个参数为 被吸附元素的left - 自身的宽度 //type==1是为拉伸功能逻辑:checkPoint()第一个参数为 自身元素的leftbool = checkPoint(type ? ($(item).position().left) : ($(target).position().left - $(item).outerWidth()),($(target).position().left),$(this).position().left,($(this).position().left + $(this).outerWidth()));if(bool) {return false;}} else {//将要右吸附,对假设已完成右吸附的元素做容器类的重叠检测bool = checkPoint(($(target).position().left + $(target).outerWidth()),($(target).position().left + $(target).outerWidth() + $(item).outerWidth()),($(this).position().left),($(this).position().left + $(this).outerWidth()));if(bool) {return false;}}if(bool) {return false;}}});//如果为true则需要重置return bool || (can < 0) || (boxRight === false ? false : (can > box.width())); }
以上是拖拽任务的吸附处理逻辑代码,其中eachItemsLR( )方法包含对拉伸功能的处理逻辑。
下面介绍拉伸功能。
首先在任务对象元素的最右边放一个拉伸所用的游标:<div class="rightLine"> 他的 position:absolute;right:0 !important;width:5px;height:100%;
以确保游标永远在拖拽元素的最右边。当拉伸游标时同时改变父元素(拖拽元素)的宽度,从而做到手动改变元素的宽度。
拉伸和拖拽功能逻辑处理上大同小异,稍稍有些不同:
1. 拉伸会改变元素的宽度。
2. 拉伸释放的瞬间,在与重叠元素做吸附判断时,如果吸附失败( 吸附后会与其他元素重叠或者超出父元素范围 ),
那么需要同时重置拉伸元素宽度和位置到拉伸前的状态(鼠标松开后需要恢复到鼠标按下之前的状态)。
//拉伸 $(".rightLine").draggable({axis: "x", cursor: "e-resize", //鼠标hover样式为拉伸drag: function(data, data1) {data1.helper.parent().parent().outerWidth(data1.position.left + 5);},stop: function(data, data1) {//拉伸任务对象var zj = data1.helper.parent().parent();if(data1.position.left < 0) {//游标left超出拖拽对象左侧(left<=)时重置游标left为0zj.outerWidth(5);data1.helper.css('left', 0);return;}var mostTouchDom = getMostTouchDom(zj);if(mostTouchDom != false) {var thisLeft = $(mostTouchDom.dom).position().left;var zjWidth = zj.outerWidth();var thisWidth = $(mostTouchDom.dom).outerWidth();if(mostTouchDom.touchRange >= 0) {if(!eachItemsLR(1, zj[0], mostTouchDom.dom, 0, ((thisLeft - zjWidth) < 0 ? 0 : (thisLeft - zjWidth)))) {zj.width($(mostTouchDom.dom).position().left - zj.position().left);data1.helper.css('left', ($(mostTouchDom.dom).position().left - zj.position().left - 5));} else {zj.outerWidth(data1.originalPosition.left + 5);data1.helper.css('left', data1.originalPosition.left);}} else {if(!eachItemsLR(1, zj[0], mostTouchDom.dom, 1, (thisLeft + zjWidth + thisWidth), zj.parent())) {zj.css('left', ($(mostTouchDom.dom).position().left + $(mostTouchDom.dom).outerWidth()));} else {zj.outerWidth(data1.originalPosition.left + 5);data1.helper.css('left', data1.originalPosition.left);}}}} });
这里需要注意的是游标向左拉到底时,任务元素的宽度要保持一个游标的宽度:5px,并且游标超出任务元素对象最左侧(left<0)时要重置游标left为0。
插件预览地址:https://zhuxiao2004.github.io/draggable/
项目是基于jqueryUI,地址为 https://github.com/zhuxiao2004/draggable
代码我会继续完善,做到每天进步一点点,谢谢!
转载于:https://www.cnblogs.com/zhuxiaoweb/p/8404438.html
实现可拖拽,拉伸,吸附功能的甘特图(时间/任务表)相关推荐
- 理解事件捕获。在限制范围内拖拽div+吸附+事件捕获
一.实现的效果是在限制范围内拖拽div+吸附+事件捕获. 这里需要理解的是事件捕获,这个事件捕获也是为了兼容div在拖拽过程中,文本不被选中这个问题. 如此良辰美景,拖拽也可以很洒脱哈.先看看图, 二 ...
- java使用重绘实现拖动_原生JS使用Canvas实现拖拽式绘图功能
一.实现的功能 1.基于oop思想构建,支持坐标点.线条(由坐标点组成,包含方向).多边形(由多个坐标点组成).圆形(包含圆心坐标点和半径)等实体 2.原生JavaScript实现,不依赖任何第三方j ...
- C#实现树型结构TreeView节点拖拽的简单功能,附全部源码,供有需要的参考
为什么80%的码农都做不了架构师?>>> 应用软件是否好用就体现在一些细节操作上,开发人员是否考虑到了很多细节,例如一个树形结构的数据若不支持拖拽功能那用起来会很糟糕一些,用户 ...
- java鼠标左键按下后拖动实现多选_鼠标拖拽多选功能
鼠标拖拽多选功能 *{ box-sizing:border-box; } ul{ width:500px; height:auto; margin:0; padding:20px; font-size ...
- 安卓开发仿微信图片拖拽_Android 仿微信朋友圈发表图片拖拽和删除功能
朋友圈实现原理 我们使用 Android Device Monitor 来分析朋友圈发布图片的界面实现原理.如果需要分析其他应用的界面实现也是采用这种方法哦. 打开 Android Device Mo ...
- Android 仿微信朋友圈发表图片拖拽和删除功能
朋友圈实现原理 我们使用 Android Device Monitor 来分析朋友圈发布图片的界面实现原理.如果需要分析其他应用的界面实现也是采用这种方法哦. 打开 Android Device Mo ...
- 安卓开发仿微信图片拖拽_仿微信朋友圈发表图片拖拽和删除功能
原标题:仿微信朋友圈发表图片拖拽和删除功能 中国联通在香港公布了上市公司2017年中期业绩.2017年上半年,公司主要业绩指标持续向好,收入稳步回升,服务收入达到人民币1,241.1亿元,同比增长3. ...
- unity2d里实现鼠标拖拽物体的功能
在 Unity 中实现鼠标拖拽物体的功能需要使用到 Unity 的 Physics 系统. 要实现鼠标拖拽物体,你需要在场景中添加以下内容: 一个 Rigidbody 2D 组件,用于控制物体的运动. ...
- vue中实现拖拽调整顺序功能
一.使用vuedraggable是标准的组件式封装 或 vue-dragging 指令方式 实现拖拽调整顺序功能. 1:安装依赖 npm install vuedraggable或yarn add v ...
最新文章
- Codeforces40E[Number Table]
- Proteus仿真STM32F103R6的寄存器版跑马灯程序(存储器宏定义)
- 一章: CentOS6.5 网络配置、修改主机名、添加硬盘、压缩——解压方法、VNC—server配置
- chmod g+s 、chmod o+t 、chmod u+s
- 技术动态 | 事理图谱,下一代知识图谱
- 基于自然语言识别下的流失用户预警
- 史上最全:PostgreSQL DBA常用SQL查询语句(建议收藏学习)
- python进阶18正则表达式
- OpenCV-人像—酷感冷艳滤镜
- Python OpenCV显示图像并保存图像
- 线性同余法产生均匀随机数C语言,利用线性同余法产生随机数进行同步计算
- Maven项目 整合 tomcat插件== tomcat7:run
- 苹果App Store搜索出Bug,网友:完美避开所有正确答案
- 路由表原理以及数据包进入路由器,路由器的处理
- Android Studio3.0以上 apk调试运行没有问题 apk安装运行闪退
- 逼疯程序员的20个瞬间
- TCP第四次挥手后为什么要等待2MSL后才断开链接?等待时间为什么是2MSL?
- 西工大NOJ数据结构理论——007.表达式括号与匹配(严3.19)
- LASSO回归与L1正则化 西瓜书
- 概率论:魏布斯分布Weibull cumulative distribution function
热门文章
- php websocket ipv6,原生socket支持ipv6
- 接口测试——测试用例执行
- java 删 除文件操作,C语言 File文件处理 删除文件
- python转json中文乱码_python 序列化成json 乱码问题的解决
- 不显示参数名_非参数检验 之 非参数卡方检验
- 重新安装Jenkins
- 如何优雅的对列表接口进行分页?
- 山东女子学院计算机专业分数线,山东女子学院2020年山东省本科专业录取分数统计(含位次)...
- java轮播图_RxJava 实现ViewPager的轮播图
- oracle 树形结构表,树结构表递归查询在ORACLE和MSSQL中的实现方法