html 树形图可拖拽,HTML5拖拽API实现vue树形拖拽组件
因業(yè)務(wù)場景需要一個可拖拽修改節(jié)點(diǎn)位置的樹形組件,因此動手?jǐn)]了一個,乘此機(jī)會摸了一把html5原生拖拽。近期有時間將核心部分代碼抽出,簡單說下實現(xiàn)方式。
1.樹形結(jié)構(gòu)-組件遞歸使用
樹形結(jié)構(gòu)非常簡單,tree組件作為父組件,結(jié)構(gòu)如下
tree.vue
復(fù)制代碼
vue組件允許在它們自己的模板中調(diào)用自身,因此可以形成樹形結(jié)構(gòu),在組件中必須填寫唯一的name。
tree-node.vue
復(fù)制代碼
2.HTML5拖拽api
1.draggable屬性規(guī)定元素是否可拖動,目前Internet Explorer 9+, Firefox, Opera, Chrome, and Safari 支持 draggable 屬性
2.HTML 5 拖放api
ondragstart: 元素開始被拖動時觸發(fā) 作用在拖拽元素上
ondragenter:當(dāng)拖曳元素進(jìn)入目標(biāo)元素的時候觸發(fā)的事件,作用在目標(biāo)元素上
ondragover:拖拽元素在目標(biāo)元素上移動的時候觸發(fā)的事件,作用在目標(biāo)元素上
ondragleave:拖拽元素拖離開了目標(biāo)元素時觸發(fā),作用在目標(biāo)元素上
ondrop:被拖拽的元素在目標(biāo)元素上同時鼠標(biāo)放開觸發(fā)的事件,作用在目標(biāo)元素上
ondragend:當(dāng)拖拽完成后觸發(fā)的事件,作用在被拖曳元素上
3.拖拽節(jié)點(diǎn)
定義變量
處理拖拽節(jié)點(diǎn)需要幾個關(guān)鍵變量
當(dāng)前拖拽的節(jié)點(diǎn)
拖拽時經(jīng)過的節(jié)點(diǎn)
最終放置的節(jié)點(diǎn)
因此定義了一個用于保存拖拽信息的對象
dragOverStatus: {
overNodeKey: "",
dropPosition: "",
dragNode: {}
}
復(fù)制代碼
綁定拖拽事件
這里將ondragstart事件綁定在子元素上,將其他事件綁定在父元素上,因為在測試真機(jī)IE10的時候,發(fā)現(xiàn)ondragstart和其他事件綁定在同一個元素上,無法觸發(fā)ondragenter等事件。
復(fù)制代碼mounted() {
//綁定拖拽事件
if (this.root.draggable) {
this.$refs.draggAbleDom.draggable = !this.nodeData.noDrag;
this.$refs.draggAbleDom.ondragstart = this.onDragStart;
this.$refs.dropTarget.ondragenter = this.onDragEnter;
this.$refs.dropTarget.ondragover = this.onDragOver;
this.$refs.dropTarget.ondragleave = this.onDragLeave;
this.$refs.dropTarget.ondrop = this.onDrop;
this.$refs.dropTarget.ondragend = this.onDragEnd;
}
}
復(fù)制代碼
觸發(fā)某節(jié)點(diǎn)的拖拽事件時,就可以從拖拽事件里拿到當(dāng)前節(jié)點(diǎn)實例。
使用HTML5提供的專門的拖拽與拖放API,原生的實現(xiàn)了復(fù)雜的操作,不需要自己用鼠標(biāo)事件模擬,因此實現(xiàn)拖拽效果非常簡單。
(1).開始拖拽:在拖拽元素上觸發(fā),事件內(nèi)只需要保存當(dāng)前拖拽節(jié)點(diǎn)的信息即可
onDragStart(e, treeNode) {
this.dragOverStatus.dragNode = {
nodeData: treeNode.nodeData,
parentNode: treeNode.parentNodeData
};
this.$emit("on-dragStart", {
treeNode: treeNode.nodeData,
parentNode: treeNode.parentNodeData,
event: e
});
}
復(fù)制代碼
(2).進(jìn)入目標(biāo)節(jié)點(diǎn):在目標(biāo)元素上觸發(fā),主要保存當(dāng)前經(jīng)過的節(jié)點(diǎn)的key,然后向外層發(fā)出事件,供組件調(diào)用者做其他操作。為了避免拖拽一個元素快速經(jīng)過許多個節(jié)點(diǎn)時頻繁發(fā)出事件,設(shè)置定時器當(dāng)停留一定時間后觸發(fā)。
onDragEnter(e, treeNode) {
//當(dāng)沒有設(shè)置拖拽節(jié)點(diǎn)時,禁止作為目標(biāo)節(jié)點(diǎn)
if (!this.hasDragNode()) {
return;
}
this.dragOverStatus.overNodeKey = "";
//拖拽節(jié)點(diǎn)與目標(biāo)節(jié)點(diǎn)是同一個,return掉
if (
treeNode.nodeData._hash === this.dragOverStatus.dragNode.nodeData._hash
) {
return;
}
this.dragOverStatus.overNodeKey = treeNode.nodeData._hash; //當(dāng)前經(jīng)過的可放置的節(jié)點(diǎn)的key
//當(dāng)前節(jié)點(diǎn)禁止做為放置節(jié)點(diǎn)時
if (treeNode.nodeData.noDrop) {
return;
}
//設(shè)置dragEnter定時器,停留250毫秒后觸發(fā)事件
if (!this.delayedDragEnterLogic) {
this.delayedDragEnterLogic = {};
}
Object.keys(this.delayedDragEnterLogic).forEach(key => {
clearTimeout(this.delayedDragEnterLogic[key]);
});
this.delayedDragEnterLogic[
treeNode.nodeData._hash
] = setTimeout(() => {
if (!treeNode.nodeData.isExpand) {
treeNode.toggleCollapseStatus();
}
this.$emit("on-dragEnter", {
treeNode: treeNode.nodeData,
parentNode: treeNode.parentNodeData,
event: e
});
}, 250);
}
復(fù)制代碼
(3).在目標(biāo)節(jié)點(diǎn)上經(jīng)過:在目標(biāo)元素上觸發(fā),即時計算鼠標(biāo)在目標(biāo)節(jié)點(diǎn)上的位置,用于判斷最終的放置位置,0(作為目標(biāo)節(jié)點(diǎn)的子節(jié)點(diǎn)),-1(放置在目標(biāo)節(jié)點(diǎn)的前面),1(放置在目標(biāo)節(jié)點(diǎn)的后面),顯示相應(yīng)的樣式。
onDragOver(e, treeNode) {
//當(dāng)沒有設(shè)置拖拽節(jié)點(diǎn)時,禁止作為目標(biāo)節(jié)點(diǎn)
if (!this.hasDragNode()) {
return;
}
if (
this.dragOverStatus.overNodeKey === treeNode.nodeData._hash
) {
this.dragOverStatus.dropPosition = this.calDropPosition(e); //放置標(biāo)識0,-1,1
}
this.$emit("on-dragOver", {
treeNode: treeNode.nodeData,
parentNode: treeNode.parentNodeData,
event: e
});
this.dragOverClass = this.setDragOverClass();//設(shè)置鼠標(biāo)經(jīng)過樣式
},
復(fù)制代碼
當(dāng)鼠標(biāo)處于目標(biāo)節(jié)點(diǎn)內(nèi)目標(biāo)節(jié)點(diǎn)偏上方(1/5處),則意為放在目標(biāo)節(jié)點(diǎn)前面-同級,當(dāng)鼠標(biāo)處于目標(biāo)節(jié)點(diǎn)內(nèi)目標(biāo)節(jié)點(diǎn)偏下方(1/5處),意為放在目標(biāo)節(jié)點(diǎn)后面-同級,否則作為目標(biāo)節(jié)點(diǎn)的子節(jié)點(diǎn)
calDropPosition(e) {
var offsetTop = this.getOffset(e.target).top;
var offsetHeight = e.target.offsetHeight;
var pageY = e.pageY;
var gapHeight = 0.2 * offsetHeight;
if (pageY > offsetTop + offsetHeight - gapHeight) {
//放在目標(biāo)節(jié)點(diǎn)后面-同級
return 1;
}
if (pageY < offsetTop + gapHeight) {
//放在目標(biāo)節(jié)點(diǎn)前面-同級
return -1;
}
//放在目標(biāo)節(jié)點(diǎn)里面-作為子節(jié)點(diǎn)
return 0;
}
復(fù)制代碼
(4).放置節(jié)點(diǎn):在目標(biāo)元素上觸發(fā),此時將拖拽的信息變量作為參數(shù)將事件發(fā)射到外層,其余操作由外層來決定即可。
onDrop(e, treeNode) {
//當(dāng)沒有設(shè)置拖拽節(jié)點(diǎn)時,禁止作為目標(biāo)節(jié)點(diǎn)
if (!this.hasDragNode()) {
return;
}
//當(dāng)前節(jié)點(diǎn)禁止拖拽時
if (treeNode.nodeData.noDrop) {
return;
}
//拖拽節(jié)點(diǎn)與目標(biāo)節(jié)點(diǎn)是同一個,不做任何操作
if (
this.dragOverStatus.dragNode.nodeData._hash === treeNode.nodeData._hash
) {
return;
}
var res = {
event: e,
dragNode: this.dragOverStatus.dragNode,
dropNode: {
nodeData: treeNode.nodeData,
parentNode: treeNode.parentNodeData
},
dropPosition: this.dragOverStatus.dropPosition
};
this.$emit("on-drop", res);
}
復(fù)制代碼
(5).拖拽結(jié)束:作用在拖拽元素上,拖拽結(jié)束后將清除變量,恢復(fù)樣式。
onDragEnd(e, treeNode) {
//當(dāng)沒有設(shè)置拖拽節(jié)點(diǎn)時,禁止作為目標(biāo)節(jié)點(diǎn)
if (!this.hasDragNode()) {
return;
}
//當(dāng)前節(jié)點(diǎn)禁止拖拽時
if (treeNode.nodeData.noDrop) {
return true;
}
this.dragOverStatus.dragNode = null;
this.dragOverStatus.overNodeKey = "";
this.$emit("on-dragEnd", {
treeNode: treeNode.nodeData,
parentNode: treeNode.parentNodeData,
event: e
});
}
復(fù)制代碼
4.應(yīng)用
調(diào)用樹形拖拽組件,獲取拖拽過程中的拖拽節(jié)點(diǎn),目標(biāo)節(jié)點(diǎn),以及放置位置,具體處理拖拽結(jié)果由調(diào)用方?jīng)Q定,可以是通過調(diào)接口更新樹結(jié)構(gòu),也可以由前端處理輸入數(shù)據(jù),更新視圖。
復(fù)制代碼getDropData(info) {
var dragData = info.dragNode.nodeData;
var dragParent = info.dragNode.parentNode;
var dropData = info.dropNode.nodeData;
var dropParent = info.dropNode.parentNode;
var dropPosition = info.dropPosition; //0作為子級,-1放在目標(biāo)節(jié)點(diǎn)前面,1放在目標(biāo)節(jié)點(diǎn)后面
//把拖拽元素從父節(jié)點(diǎn)中刪除
dragParent.children.splice(dragParent.children.indexOf(dragData), 1);
if (dropPosition === 0) {
dropData.children.push(dragData);
} else {
var index = dropParent.children.indexOf(dropData);
if (dropPosition === -1) {
dropParent.children.splice(index, 0, dragData);
} else {
dropParent.children.splice(index + 1, 0, dragData);
}
}
}
復(fù)制代碼
作為子節(jié)點(diǎn),改變層級
修改排序,將拖拽節(jié)點(diǎn)放在目標(biāo)節(jié)點(diǎn)后面
修改排序,將拖拽節(jié)點(diǎn)放在目標(biāo)節(jié)點(diǎn)前面
總結(jié)
以上是生活随笔為你收集整理的html 树形图可拖拽,HTML5拖拽API实现vue树形拖拽组件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 24.JSP 客户端请求
- 下一篇: html5与跨平台开发,HTML5应用与