因為項目有rem適配,使用第三方插件無法處理適配問題,所有只能自己寫拖拽功能了
拖拽一般都會想到按下,移動,放開,但是本人親測,就在div綁定一個按下事件就行了(在事件里面寫另外兩個事件),另外兩個綁上,會莫名其妙卡死,那種莫名其妙的問題
推薦幾個開發調試時使用的第三方拖動插件吧,雖然沒用上,但是他們是真的好vue-drag-resize
和vuedraggable
,其中前者更輕量化,后者功能更全
主要功能:
效果圖:
界面:(就是大的父盒子包著幾個小盒子,盒子里面有圖片和文字)
<div class="range" id="range" ref="range"><divclass="iconItem"v-for="(item, index) in pointList":key="index"@mousedown.stop.prevent.native="mousedown($event, item)":style="{left: item.dx + 'px',top: item.dy + 'px','z-index': item.zIndex,}"><!--@mousemove.stop.prevent.native="mousemove($event, item)"@mouseup.stop.prevent.native="mouseup($event, item)"--><imgdraggable="false":src="typeList[item.type].src":alt="typeList[item.type].name + item.EName"/><span>{{ typeList[item.type].name + item.EName }}</span></div></div>
邏輯:
<script setup lang="ts">
import { ref, reactive, watch, computed, Ref } from "vue";
import { mapPunctuation } from "@/utils/youran";
let rem = ref(0.005208); // 10/1920 做好功能給上面的left top乘上去就行了 left: item.dx * rem + 'px'const range: Ref = ref(null);// 這里只是把存在文件里的base64圖片文件取出來,
let typeList = reactive([{type: 1,src: "",name: "球機-攝像頭",},{type: 2,src: "",name: "搶機-攝像頭",},{type: 3,src: "",name: "無源打卡設備",},{type: 4,src: "",name: "無源打卡設備",},{type: 5,src: "",name: "反向控制",},
]);typeList.forEach((item, index) => {item.src = mapPunctuation[index].src;
});let pointList = ref([{fId: "111",type: 1,EId: "",EName: "",dx: 0,dy: 0,zIndex: 2,},
]);// 鼠標事件
let downType = ref(false);
let disX = 0;
let disY = 0;
let odiv: any = null;
let mousedown = (e: any, item: any) => {downType.value = true;console.log("按下事件");odiv = e.target;disX = e.clientX - odiv.offsetLeft;disY = e.clientY - odiv.offsetTop;document.onmousemove = (e) => {console.log("移動事件");//計算元素位置(需要判斷臨界值)let left = e.clientX - disX;let top = e.clientY - disY;let { offsetHeight: pondModelHeight, offsetWidth: pondModelWidth } =range.value;let { offsetHeight: sonNodeHeight, offsetWidth: sonNodeWidth } = odiv;// 左上角(left)if (left < 0) {left = 0;}if (top < 0) {top = 0;}// 左下角if (top > pondModelHeight - sonNodeHeight) {top = pondModelHeight - sonNodeHeight;}if (left > pondModelWidth - sonNodeWidth) {left = pondModelWidth - sonNodeWidth;}item.dx = left;item.dy = top;item.zIndex = 999;};document.onmouseup = (e) => {console.log("放開事件");document.onmousemove = null;document.onmouseup = null;item.zIndex = 1;odiv = null;};
};
</script>
css:本來不該放出來,但是我在這里踩坑了,覺得其他人也會(img圖片有默認的拖拽,很難禁止,所以拿一個偽元素直接放在img上面,不給點img就不會踩坑)
.range {width: 960px;height: 540px;background-color: pink;position: relative;.iconItem {position: absolute;left: 10px;top: 10px;z-index: 2;display: flex;align-items: center;cursor: move;user-select: none;width: 32px;height: 32px;background: yellow;img {width: 32px;height: 32px;}// 關鍵&::before {content: " ";width: 100%;height: 100%;position: absolute;top: 0;left: 0;z-index: 3;}&:hover {// span {// display: block;// }}span {display: none;font-size: 12px;font-family: YouSheBiaoTiHei;color: red;}}}
完整代碼:(建議按照上面的一點點復制吧,有幾個文件是外部的base64圖片)
<template><div class="PastureMap"><div class="mapContent"><div class="mapBox"><div class="range" id="range" ref="range"><divclass="iconItem"v-for="(item, index) in pointList":key="index"@mousedown.stop.prevent.native="mousedown($event, item)":style="{left: item.dx + 'px',top: item.dy + 'px','z-index': item.zIndex,}"><!--@mousemove.stop.prevent.native="mousemove($event, item)"@mouseup.stop.prevent.native="mouseup($event, item)"--><imgdraggable="false":src="typeList[item.type].src":alt="typeList[item.type].name + item.EName"/><span>{{ typeList[item.type].name + item.EName }}</span></div></div></div><div class="operationPanel"><div class="addIConCard"><div class="title"><span>新增圖標</span></div><div class="box"><div class="bgImg"><div class="left"><span>背景圖:</span></div><div class="right"><button>選擇圖片</button><span>建議尺寸:960*540</span></div></div><div class="iconBtnForm"><div class="cell"><div class="left"><span>圈舍</span></div><div class="right"><input type="text" placeholder="請選擇圈舍" /></div></div><div class="cell"><div class="left"><span>設備編號</span></div><div class="right"><input type="text" placeholder="請輸入設備編號" /></div></div><div class="cell"><div class="left"><span>類型</span></div><div class="right"><input type="text" placeholder="請選擇類型" /></div></div></div><div class="addBtn"><button>新增</button></div></div></div><div class="iconList"><div class="item" v-for="(item, index) in pointList" :key="index"><div class="left"><span>類型</span></div><div class="right"><input type="text" placeholder="名稱" /></div><div class="del"><img src="" alt="del" /></div></div></div></div></div></div>
</template><script setup lang="ts">
import { ref, reactive, watch, computed, Ref } from "vue";
import { mapPunctuation } from "@/utils/youran";
let rem = ref(0.005208); // 10/1920const range: Ref = ref(null);
let typeList = reactive([{type: 1,src: "",name: "球機-攝像頭",},{type: 2,src: "",name: "搶機-攝像頭",},{type: 3,src: "",name: "無源打卡設備",},{type: 4,src: "",name: "無源打卡設備",},{type: 5,src: "",name: "反向控制",},
]);typeList.forEach((item, index) => {item.src = mapPunctuation[index].src;
});let pointList = ref([{fId: "111",type: 1,EId: "",EName: "",dx: 0,dy: 0,zIndex: 2,},
]);// 鼠標事件
let downType = ref(false);
let disX = 0;
let disY = 0;
let odiv: any = null;
let mousedown = (e: any, item: any) => {downType.value = true;console.log("按下事件");odiv = e.target;disX = e.clientX - odiv.offsetLeft;disY = e.clientY - odiv.offsetTop;document.onmousemove = (e) => {console.log("移動事件");//計算元素位置(需要判斷臨界值)let left = e.clientX - disX;let top = e.clientY - disY;let { offsetHeight: pondModelHeight, offsetWidth: pondModelWidth } =range.value;let { offsetHeight: sonNodeHeight, offsetWidth: sonNodeWidth } = odiv;// 左上角(left)if (left < 0) {left = 0;}if (top < 0) {top = 0;}// 左下角if (top > pondModelHeight - sonNodeHeight) {top = pondModelHeight - sonNodeHeight;}if (left > pondModelWidth - sonNodeWidth) {left = pondModelWidth - sonNodeWidth;}item.dx = left;item.dy = top;item.zIndex = 999;};document.onmouseup = (e) => {console.log("放開事件");document.onmousemove = null;document.onmouseup = null;item.zIndex = 1;odiv = null;};
};
</script><style lang="less" scoped>
.PastureMap {height: 100%;.mapContent {display: flex;height: 100%;.mapBox {flex: 1;height: 100%;.range {width: 960px;height: 540px;background-color: pink;position: relative;.iconItem {position: absolute;left: 10px;top: 10px;z-index: 2;display: flex;align-items: center;cursor: move;user-select: none;width: 32px;height: 32px;background: yellow;img {width: 32px;height: 32px;}&::before {content: " ";width: 100%;height: 100%;position: absolute;top: 0;left: 0;z-index: 3;}&:hover {// span {// display: block;// }}span {display: none;font-size: 12px;font-family: YouSheBiaoTiHei;color: red;}}}}.operationPanel {width: 270px;.addIConCard {.title {span {}}.box {.bgImg {display: flex;align-items: center;.left {}.right {}}.iconBtnForm {.cell {display: flex;align-items: center;.left {span {}}.right {input {}}}}}}.iconList {.item {display: flex;align-items: center;position: relative;.left {span {}}.right {input {}}.del {position: absolute;top: 0;right: 0;}}}}}
}
</style>