简易而又灵活的Javascript拖拽框架(四)

一、开篇

似乎拖拽已经被写烂了,没得写的了,可是我这次又来了~

上一次写的是跨列拖放,这次我要带给大家的是跨页拖放。

可以到这里来看看效果:示例效果

说明:1、如果将方框拖动到页签上立刻释放掉的话,则会被添加到该页的第一列的第一个位置;

     2、如果将方框拖动到页签上并且停留片刻的话,则页面就会转换到该页,这个时候可以在页签上释放,也可以将方框拖动到此页的具体位置释放。

二、原理

     我是在跨列拖放的基础上修改的代码,虽然仅仅是从跨“列”升级为跨“页“,但是这就意味着多了一个dimension。所以代码改动比较大,尤其是初始化的代码。

为了弄清楚代码中的命名同时也便于阐述原理,我画了下面的图


 

 

1、在拖动开始时,跟之前的跨列拖拽差不多,基本上不需要修改;

2、在拖动的过程中,就需要判断拖动module时的鼠标是否是在某个tab上,如果在tab上,则把dragGhost(拖动中占位的虚线框)隐藏了,并且设置转换页面的timeout(注意:这个timeout不要设置重复了,而且如果鼠标就在本页上就不需要设置),在设置这个timeout的响应函数也要小心,必须先把拖动的module放到新的页面然后再转换页面,因为鼠标虽然拖动了module,但是在html代码中,这个module还是属于原来的页面(只是因为positionabsolute才让它游离出来的),如果原来的页面因为页面转换而变得不可见了,那么鼠标拖动的module也会不翼而飞的。

3、在拖动的过程中,如果鼠标不在某个tab上,首先要将timeout及时清除了,要不然在拖动时会莫名其妙的转换页面,剩下的跟页面内拖放是一样的计算方法来处理,也是先计算所在的列,然后计算再这个列的位置,在此不再累述。

4、在拖动的过程中,注意维持modulecolumn变量,这个变量对于拖放很重要,要及时而正确的更新。

5、在拖动结束的时候,如果鼠标还在某个tab上(无论这个时候页面是不是因为鼠标的停留而改变),则把module放在这一页的第一列的第一个位置。如果不在tab上,那么和页面内拖放是一样的。无论怎样,在最后都要设置一些style以及更新个别变量,放置完毕。

三、代码

       原理说起来容易,写起来还是很麻烦的,而且得经过很多次测试才能成功的。

       不过我总结出来几点:

       1、对于任何一个对象,要分清楚这个对象是html对象还是我们自定义类的对象;

       2、各种对象尽量少维持一些变量,要不然在每次动作发生的时候都会去更新一堆变量,那将是很麻烦的(或许可以将这些变量的设置封装成对象的方法);

       主要代码如下:

ContractedBlock.gifExpandedBlockStart.gifCode
var module = function(moduleElm){
    
var self = this;
    
this.elm = moduleElm;
    
this.elm.module = this;
    
this.column = moduleElm.column;
    
this.page = this.column.page;
    
this.handle = this.elm.getElementsByTagName("h3")[0];
    
//这里只是为了各个页面的module看起来不一样 所以另外设置一下style
    //page的id也是为了这个目的而加的 其他地方page的id是用不上的
    
    
switch(this.page.id){
        
case "page1":this.handle.style.backgroundColor="red";break;
        
case "page2":this.handle.style.backgroundColor="blue";break;
        
case "page3":this.handle.style.backgroundColor="black";break;
        
case "page4":this.handle.style.backgroundColor="#CCCCCC";break;
    }
    
    
    
if(this.handle && this.elm){
        Drag.init(
this.handle,this.elm);
    }
else{
        
return;
    }
    
this.elm.onDragStart = function(left,top,mouseX,mouseY){
        
//开始拖动的时候设置透明度
        
        
this.style.opacity = "0.5";
        
this.style.filter = "alpha(opacity=50)";
        dragGhost.style.height 
= isIE?this.offsetHeight:this.offsetHeight - 2;
        
        
//this指的是item
        
        
this.style.width = this.offsetWidth;//因为初始的width为auto
        this.style.left = findPosX(this- 5;
        
this.style.top = findPosY(this- 5;
        
this.style.position = "absolute";
        
        
//将ghost插入到当前位置
        
        dragGhost.style.display 
= "block";
        self.column.insertBefore(dragGhost,
this);
        
        
//记录每一列的左边距 在拖动过程中判断拖动对象所在的列会用到
        
        
this.columnsX = [];
        
for(var i=0;i<self.column.page.columns.length;i++){
            
this.columnsX.push(findPosX(self.column.page.columns[i]));
        }
            
    }
    
this.elm.onDrag = function(left,top,mouseX,mouseY){
        
this.currentTab = null;
        
//判断是否在tab上
        
        
for(var i=0;i<XDrag.tabs.length;i++){
            
var tabElm = XDrag.tabs[i].elm;
            
if((findPosX(tabElm) < mouseX) &&
                (findPosX(tabElm) 
+ tabElm.offsetWidth > mouseX) &&
                (findPosY(tabElm) 
< mouseY) &&
                (findPosY(tabElm) 
+ tabElm.offsetHeight > mouseY)){
                
this.currentTab = XDrag.tabs[i];
                
break;
            }
        }
        
if(this.currentTab != null){
            
if(dragGhost.parentNode)
                dragGhost.parentNode.removeChild(dragGhost);

            
function changeTab(){
                
//先得把module放到当前的这一页
                //否则会随着tab的改变而消失
                
                
var currentColumn = self.elm.currentTab.page.columns[0];
                
                
var flag = false;
                
for(var i=0;i<currentColumn.childNodes.length;i++){
                    
if(currentColumn.childNodes[i].nodeName.toLowerCase() == "div"){
                        currentColumn.insertBefore(self.elm,currentColumn.childNodes[i]);
                        flag 
= true;
                        
break;
                    }
                }
                
if(!flag)
                    currentColumn.appendChild(
this);
                self.column 
= currentColumn;//将拖动的module添加到这一页的第一列 因为display还为absolute 所以module还跟着鼠标在走
                
                self.elm.currentTab.select();
                XDrag.changeTabTimeoutId 
== null;
            }
            
            
//如果Timeout不为空(防止重复设置Timeout) 而且移动到的tab不是当前的tab(如果是当前tab则不需要改变tab页了)
            
            
if(XDrag.changeTabTimeoutId == null && this.currentTab != XDrag.selectedTab)
                XDrag.changeTabTimeoutId 
= setTimeout(changeTab,XDrag.changeTabTimeout);
            
return;//如果鼠标在tab上 则不必理会页面内的移动了
            
        }
        
        
//以下是计算在页面内拖拽的代码
        
        clearTimeout(XDrag.changeTabTimeoutId);
        XDrag.changeTabTimeoutId 
= null;//既然鼠标都没有在tab上了 当然就应该清空timeout了
        //先要判断在哪一列移动
        
        
var columnIndex = 0
        
        
for(var i=0;i<this.columnsX.length;i++){
            
if((left + this.offsetWidth/2) > this.columnsX[i]){
                columnIndex = i;
            }
        }
        
//如果columnIndex在循环中没有被赋值 则表示当前拖动对象在第一列的左边
        //此时也把它放到第一列
        
        
var column = self.column.page.columns[columnIndex];
        
        
if(self.column != column){
            
//之前拖动对象不在这个列
            //将ghost放置到这一列的最下方
            
            
//如果已经跨页拖放了 也会执行这里的
            
            column.appendChild(dragGhost);
            self.column 
= column;
        }
        
        
//然后在判断放在这一列的什么位置
        
        
var currentNode = null;
        
for(var i=0;i<self.column.childNodes.length;i++){
            
if(self.column.childNodes[i].className == "item"
            
&& self.column.childNodes[i] != this//不能跟拖动元素自己比较 否则不能在本列向下移动
            
            
&& top < findPosY(self.column.childNodes[i])){//从上到下找到第一个比拖动元素的上边距大的元素
            
                currentNode 
= self.column.childNodes[i];
                
break;
            }
        }
        
if(currentNode)
            self.column.insertBefore(dragGhost,currentNode);
        
else//拖到最下边 没有任何一个元素的上边距比拖动元素的top大 则添加到列的最后
        
            self.column.appendChild(dragGhost);
    }
    
this.elm.onDragEnd = function(left,top,mouseX,mouseY){
        
if(this.currentTab != null){
            
//this.currentTab != null表示鼠标拖拽的module在tab上释放 无论这个时候tab是否因为鼠标的停留而转换了页签
            
            clearTimeout(XDrag.changeTabTimeoutId);
            XDrag.changeTabTimeoutId 
= null;
            
var firstColumn = this.currentTab.page.columns[0];
            
var flag = false;
            
for(var i=0;i<firstColumn.childNodes.length;i++){
                
if(firstColumn.childNodes[i].nodeName.toLowerCase() == "div"){
                    firstColumn.insertBefore(
this,firstColumn.childNodes[i]);
                    flag 
= true;
                    
break;
                }
            }
            
if(!flag)
                firstColumn.appendChild(
this);
            self.column 
= firstColumn;
        }
else{
            self.column.insertBefore(
this,dragGhost);
        }
        
this.style.opacity = "1";
        
this.style.filter = "alpha(opacity=100)";
        
        
this.style.position = "static";
        
this.style.display = "block";
        
this.style.width = "auto";
        dragGhost.style.display 
= "none";
        self.page 
= self.column.page;//需要手动更新(奇怪 self.page难道是个值类型)
        //也可以不要最后这一句 仅仅是为了数据的完整性
        
    }
    
}

四、示例下载

      点此下载示例

转载于:https://www.cnblogs.com/LongWay/archive/2008/09/23/1297173.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/379896.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python---异常处理结构

python中提供了很多不同形式的异常处理结构&#xff0c;其基本思路都是先尝试执行代码&#xff0c;再处理可能发生的错误。 try…except… 在python异常处理结构中&#xff0c;try…except…使用最为频繁&#xff0c;其中try子句中的代码块为可能引发异常的语句&#xff0c;e…

python---Socket编程

Sockte是计算机之间进行网络通信的一套程序接口&#xff0c;相当于在发送端和接收端之间建立一个通信管道。在实际应用中&#xff0c;一些远程管理软件和网络安全软件大多数依赖于Socket来实现特定功能&#xff0c;由于TCP方式在网络编程中应用非常频繁&#xff0c;此处将对TCP…

(X)HTML嵌套规则

本文整理于互联网~ 简单认识了块元素和内嵌元素以后&#xff0c;下面就可以罗列 XHTML 标签的嵌套规则了&#xff1a; 1. 块元素可以包含内联元素或某些块元素&#xff0c;但内联元素却不能包含块元素&#xff0c;它只能包含其它的内联元素&#xff1a;<div><h1>&…

ASP.NET Web API 处理架构

这篇文章主要是介绍ASP.NET Web API的处理架构&#xff1a;当一个HTTP请求到达直到产生一个请求的过程。ASP.NET Web API 的处理架构图如下&#xff0c;主要有三层组成&#xff1a;宿主&#xff08;hosting&#xff09;&#xff0c;消息处理管道&#xff08;message handler pi…

python---可执行文件的转换

pyinstaller是常见的执行文件打包工具。该工具的安装方式非常简单&#xff0c;可运行在windows、MacOS X和GNU/Linux操作系统环境中&#xff0c;执行python2和python3。 用pyinstaller打包的执行文件&#xff0c;只能在于执行打包操作的系统类型相同的环境下运行。也就是说&…

数组排序最小复杂度_进行排序的最小缺失数

数组排序最小复杂度Problem statement: 问题陈述&#xff1a; Given an array of n integers. Find the minimum number of elements from the array to remove or delete so that when the remaining elements are placed in the same sequence order form a sorted sequence…

轻松掌握Windows窗体间的数据交互(转载)

轻松掌握Windows窗体间的数据交互作者&#xff1a;郑佐日期&#xff1a;2004-04-05Windows 窗体是用于 Microsoft Windows 应用程序开发的、基于 .NET Framework 的新平台。此框架提供一个有条理的、面向对象的、可扩展的类集&#xff0c;它使您得以开发丰富的 Windows 应用程序…

python免杀技术---shellcode的加载与执行

0x01 生成shellcode 首先通过下列命令生成一个shellcode&#xff0c;使用msfvenom -p选项来指定paylaod&#xff0c;这里选用windows/x64、exec模块接收的参数。使用calc.exe执行弹出计算器的操作。-f选项用来执行生成的shellcdoe的编译语言。 msfvenom -p windows/x64/exec …

Qt中QTableWidget用法总结

QTableWidget是QT程序中常用的显示数据表格的空间&#xff0c;很类似于VC、C#中的DataGrid。说到QTableWidget&#xff0c;就必须讲一下它跟QTabelView的区别了。QTableWidget是QTableView的子类&#xff0c;主要的区别是QTableView可以使用自定义的数据模型来显示内容(也就是先…

[转]软件架构师书单

"其实中国程序员&#xff0c;现在最需要的是一张安静的书桌。"&#xff0c;的确&#xff0c;中国架构师大多缺乏系统的基础知识&#xff0c;与其自欺欺人的宣扬"读书无用&#xff0c;重在实践变通&#xff0c;修身立命哲学书更重要"&#xff0c;把大好时间…

python免杀技术---复现+改进----1

0x01 复现 复现文章&#xff1a;https://mp.weixin.qq.com/s?__bizMzI3MzUwMTQwNg&mid2247484733&idx2&sn5b8f439c2998ce089eb44541d2da7a15&chksmeb231%E2%80%A6 首先用cobaltstruke生成一个python的payload脚本 然后复制里面的payload进行Base64编码&…

python安全攻防---信息收集---IP查询

IP查询是通过当前所获得的URL去查询对应IP地址的过程&#xff0c;可应用Socket库函数中的gethostbyname()获取域名所对用的IP值 程序如下&#xff1a; # -*- coding:utf-8 -*- IP查询import socket ip socket.gethostbyname(www.baidu.com) print(ip)运行结果&#xff1a; …

智能课程表Android版-学年学期星期的实现

上次我们实现了日期和时间的动态显示&#xff0c;这次我们来实现学年&#xff0c;学期&#xff0c;周次的显示&#xff0c;如图: 首先是学年学期的显示&#xff1a; Calendar cCalendar.getInstance(); int yearc.get(Calendar.YEAR); int monthc.get(Calendar.MONTH)1;//Calen…

python安全攻防---信息收集---whois查询

whois是用来查询域名的IP以及所有者信息的传输协议。简单地说&#xff0c;whois就是一个数据库&#xff0c;用来查询域名是否以及被注册&#xff0c;以及注册域名的详细信息&#xff08;如域名所有人、域名注册商等&#xff09;。 使用whois查询&#xff0c;首先通过pip安装py…

百度面试题:从输入url到显示网页,后台发生了什么?

参考http://igoro.com/archive/what-really-happens-when-you-navigate-to-a-url/ http://www.cnblogs.com/wenanry/archive/2010/02/25/1673368.html 原文:http://igoro.com/archive/what-really-happens-when-you-navigate-to-a-url/ 作为一个软件开发者&#xff0c;你一定会…

python安全攻防---爬虫基础---get和post提交数据

get提交数据1 get提交的数据就附在提交给服务器的url之后&#xff0c;以&#xff1f;开头参数之间以&隔开&#xff0c;例如/admin/user/123456.aspx?name123&id123 案例&#xff1a;写个脚本&#xff0c;在sogou自动搜索周杰伦&#xff0c;并将搜索页面的数据获取 程…

python安全攻防---爬虫基础--re解析数据

0x01 re基础 使用re模块&#xff0c;必须先导入re模块 import refindall()&#xff1a;匹配所有符合正则的内容&#xff0c;返回的是一个列表 import restr "我的电话&#xff1a;10086&#xff0c;女朋友电话&#xff1a;11011" list re.findall(\d,str) print…

python安全攻防---爬虫基础---BeautifulSoup解析

0x01 基础 使用bs4首先要安装&#xff0c;安装后导入 import bs4bs对象有两个方法&#xff0c;一个是find&#xff0c;另一个是find_all find&#xff08;标签名&#xff0c;属性值&#xff09;&#xff1a;只返回一个&#xff0c;返回也是bs对象&#xff0c;可以继续用find…

Java——Socket通信原理

* Socket通信原理图解* A:Socket(中文翻译为&#xff1a;电源插座)套接字概述* 网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识套接字* 通信的两端都有Socket(两端都是电源插座&#xff0c;中间是啥&#xff1f;不就是电线啦&#xff0c;电…

python安全攻防---scapy基础---计算机网络各层协议

网络层次划分 比较常用的是TCP/IP五层协议。 0x01应用层 应用层是网络应用程序以及它们的应用层协议存留的地方。应用层协议和应用程序直接挂钩 DHCP(Dynamic Host Configuration Protocol)动态主机分配协议&#xff0c;使用 UDP 协议工作&#xff0c;主要有两个用途&#xf…