亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

使用vue實現grid-layout功能實例代碼

2019-11-19 14:35:41
字體:
來源:轉載
供稿:網友

1.先clone項目到本地。

2.git reset --hard commit 命令可以使當前head指向某個commit。

完成html的基本布局

點擊復制按鈕來復制整個commit id。然后在項目根路徑下運行 git reset 。用瀏覽器打開index.html來預覽效果,該插件的html主要結果如下:

<!-- 節點容器 --><div class="dragrid"> <!-- 可拖拽的節點,使用translate控制位移 --> <div class="dragrid-item" style="transform: translate(0px, 0px)"> <!-- 通過slot可以插入動態內容 --> <div class="dragrid-item-content">   </div> <!-- 拖拽句柄 --> <div class="dragrid-drag-bar"></div> <!-- 縮放句柄 --> <div class="dragrid-resize-bar"></div> </div></div>

使用vue完成nodes簡單排版

先切換commit,安裝需要的包,運行如下命令:

git reset --hard 83842ea107e7d819761f25bf06bfc545102b2944npm install<!-- 啟動,端口為7777,在package.json中可以修改 -->npm start

這一步一個是搭建環境,這個直接看webpack.config.js配置文件就可以了。

另一個就是節點的排版(layout),主要思路是把節點容器看成一個網格,每個節點就可以通過橫坐標(x)和縱坐標(y)來控制節點的位置,左上角坐標為(0, 0);通過寬(w)和高(h)來控制節點大??;每個節點還必須有一個唯一的id。這樣節點node的數據結構就為:

{ id: "uuid", x: 0, y: 0, w: 6, h: 8}

其中w和h的值為所占網格的格數,例如容器是24格,且寬度為960px,每格寬度就為40px,則上面節點渲染為240px * 320px, 且在容器左上角。

來看一下dragrid.vue與之對應的邏輯:

computed: { cfg() { let cfg = Object.assign({}, config); cfg.cellW = Math.floor(this.containerWidth / cfg.col); cfg.cellH = cfg.cellW; // 1:1 return cfg; }},methods: { getStyle(node) { return {  width: node.w * this.cfg.cellW + 'px',  height: node.h * this.cfg.cellH + 'px',  transform: "translate("+ node.x * this.cfg.cellW +"px, "+ node.y * this.cfg.cellH +"px)" }; }}

其中cellW、cellH為每個格子的寬和高,這樣計算節點的寬和高及位移就很容易了。

完成單個節點的拖拽

拖拽事件

1.使用mousedown、mousemove、mouseup來實現拖拽。

2.這些事件綁定在document上,只需要綁定一次就可以。

執行流程大致如下:

鼠標在拖拽句柄上按下, onMouseDown 方法觸發,在eventHandler中存儲一些值之后,鼠標移動則觸發 onMouseMove 方法,第一次進入時 eventHandler.drag 為false,其中isDrag方法會根據位移來判斷是否是拖拽行為(橫向或縱向移動5像素),如果是拖拽行為,則將drag屬性設置為true,同時執行 dragdrop.dragStart 方法(一次拖拽行為只會執行一次),之后鼠標繼續移動,則就開始執行 dragdrop.drag 方法了。最后鼠標松開后,會執行 onMouseUp 方法,將一些狀態重置回初始狀態,同時執行 dragdrop.dragEnd 方法。

拖拽節點

拖拽節點的邏輯都封裝在dragdrop.js這個文件里,主要方法為 dragStart 、 drag 、 dragEnd 。

dragStart

在一次拖拽行為中,該方法只執行一次,因此適合做一些初始化工作,此時代碼如下:

dragStart(el, offsetX, offsetY) { // 要拖拽的節點 const dragNode = utils.searchUp(el, 'dragrid-item'); // 容器 const dragContainer = utils.searchUp(el, 'dragrid'); // 拖拽實例 const instance = cache.get(dragContainer.getAttribute('name')); // 拖拽節點 const dragdrop = dragContainer.querySelector('.dragrid-dragdrop'); // 拖拽節點id const dragNodeId = dragNode.getAttribute('dg-id'); // 設置拖拽節點 dragdrop.setAttribute('style', dragNode.getAttribute('style')); dragdrop.innerHTML = dragNode.innerHTML; instance.current = dragNodeId; const offset = utils.getOffset(el, dragNode, {offsetX, offsetY}); // 容器偏移 const containerOffset = dragContainer.getBoundingClientRect(); // 緩存數據 this.offsetX = offset.offsetX; this.offsetY = offset.offsetY; this.dragrid = instance; this.dragElement = dragdrop; this.dragContainer = dragContainer; this.containerOffset = containerOffset;}

1.參數el為拖拽句柄元素,offsetX為鼠標距離拖拽句柄的橫向偏移,offsetY為鼠標距離拖拽句柄的縱向偏移。

2.通過el可以向上遞歸查找到拖拽節點(dragNode),及拖拽容器(dragContainer)。

3.dragdrop元素是真正鼠標控制拖拽的節點,同時與之對應的布局節點會變為占位節點(placeholder),視覺上顯示為陰影效果。

4.設置拖拽節點其實就將點擊的dragNode的innerHTML設置到dragdrop中,同時將樣式也應用過去。

5.拖拽實例,其實就是dragrid.vue實例,它在created鉤子函數中將其實例緩存到cache中,在這里根據name就可以從cache中得到該實例,從而可以調用該實例中的方法了。

6.instance.current = dragNodeId; 設置之后,dragdrop節點及placeholder節點的樣式就應用了。

7.緩存數據中的offsetX、offsetY是拖拽句柄相對于節點左上角的偏移。

drag

發生拖拽行為之后,鼠標move都會執行該方法,通過不斷更新拖拽節點的樣式來是節點發生移動效果。

drag(event) { const pageX = event.pageX, pageY = event.pageY; const x = pageX - this.containerOffset.left - this.offsetX,  y = pageY - this.containerOffset.top - this.offsetY; this.dragElement.style.cssText += ';transform:translate('+ x +'px, '+ y +'px)';}

主要是計算節點相對于容器的偏移:鼠標距離頁面距離-容器偏移-鼠標距離拽節點距離就為節點距離容器的距離。

dragEnd

主要是重置狀態。邏輯比較簡單,就不再細說了。

到這里已經單個節點已經可以跟隨鼠標進行移動了。

使placeholder可以跟隨拖拽節點運動

本節是要講占位節點(placeholder陰影部分)跟隨拖拽節點一起移動。主要思路是:

通過拖拽節點距離容器的偏移(drag方法中的x, y),可以將其轉化為對應網格的坐標。

轉化后的坐標如果發生變化,則更新占位節點的坐標。

drag方法中增加的代碼如下:

// 坐標轉換const nodeX = Math.round(x / opt.cellW);const nodeY = Math.round(y / opt.cellH);let currentNode = this.dragrid.currentNode;// 發生移動if(currentNode.x !== nodeX || currentNode.y !== nodeY) { currentNode.x = nodeX; currentNode.y = nodeY;}

nodes重排及上移

本節核心點有兩個:

用一個二維數組來表示網格,這樣節點的位置信息就可以在此二維數組中標記出來了。

nodes中只要某個節點發生變化,就要重新排版,要將每個節點盡可能地上移。

二維數組的構建

getArea(nodes) { let area = []; nodes.forEach(n => { for(let row = n.y; row < n.y + n.h; row++){  let rowArr = area[row];  if(rowArr === undefined){  area[row] = new Array();  }  for(let col = n.x; col < n.x + n.w; col++){  area[row][col] = n.id;  } } }); return area;}

按需可以動態擴展該二維數據,如果某行沒有任何節點占位,則實際存儲的是一個undefined值。否則存儲的是節點的id值。

布局方法

dragird.vue中watch了nodes,發生變化后會調用layout方法,代碼如下:

/** * 重新布局 * 只要有一個節點發生變化,就要重新進行排版布局 */layout() { this.nodes.forEach(n => { const y = this.moveup(n); if(y < n.y){  n.y = y; } });},// 向上查找節點可以冒泡到的位置moveup(node) { let area = this.area; for(let row = node.y - 1; row > 0; row--){ // 如果一整行都為空,則直接繼續往上找 if(area[row] === undefined) continue; for(let col = node.x; col < node.x + node.w; col++){  // 改行如果有內容,則直接返回下一行  if(area[row][col] !== undefined){  return row + 1;  } } } return 0;}

布局方法layout中遍歷所有節點,moveup方法返回該節點縱向可以上升到的位置坐標,如果比實際坐標小,則進行上移。moveup方法默認從上一行開始找,直到發現二維數組中存放了值(改行已經有元素了),則返回此時行數加1。

到這里,拖拽節點移動時,占位節點會盡可能地上移,如果只有一個節點,那么占位節點一直在最上面移動。

相關節點的下移

拖拽節點移動時,與拖拽節點發生碰撞的節點及其下發的節點,都先下移一定距離,這樣拖拽節點就可以移到相應位置,最后節點都會發生上一節所說的上移。

請看dragrid.vue中的overlap方法:

overlap(node) { // 下移節點 this.nodes.forEach(n => { if(node !== n && n.y + n.h > node.y) {  n.y += node.h; } });}

n.y + n.h > node.y 表示可以與拖拽節點發生碰撞,以及在拖拽節點下方的節點。

在dragdrop.drag中會調用該方法。

注意目前該方法會有問題,沒有考慮到如果碰撞節點比較高,則 n.y += node.h 并沒有將該節點下沉到拖拽節點下方,從而拖拽節點會疊加上去。后面會介紹解決方法。

縮放

上面的思路都理解之后,縮放其實也是一樣的,主要還是要進行坐標轉換,坐標發生變化后,就會調用overlap方法。

resize(event) { const opt = this.dragrid.cfg; // 之前 const x1 = this.currentNode.x * opt.cellW + this.offsetX,  y1 = this.currentNode.y * opt.cellH + this.offsetY; // 之后 const x2 = event.pageX - this.containerOffset.left,  y2 = event.pageY - this.containerOffset.top; // 偏移 const dx = x2 - x1, dy = y2 - y1; // 新的節點寬和高 const w = this.currentNode.w * opt.cellW + dx,  h = this.currentNode.h * opt.cellH + dy; // 樣式設置 this.dragElement.style.cssText += ';width:' + w + 'px;height:' + h + 'px;'; // 坐標轉換 const nodeW = Math.round(w / opt.cellW); const nodeH = Math.round(h / opt.cellH); let currentNode = this.dragrid.currentNode; // 發生移動 if(currentNode.w !== nodeW || currentNode.h !== nodeH) {  currentNode.w = nodeW;  currentNode.h = nodeH;  this.dragrid.overlap(currentNode); }}

根據鼠標距拖拽容器的距離的偏移,來修改節點的大小(寬和高),其中x1為鼠標點擊后距離容器的距離,x2為移動一段距離之后距離容器的距離,那么差值dx就為鼠標移動的距離,dy同理。

到這里,插件的核心邏輯基本上已經完成了。

[fix]解決碰撞位置靠上的大塊,并沒有下移的問題

overlap修改為:

overlap(node) { let offsetUpY = 0; // 碰撞檢測,查找一起碰撞節點里面,位置最靠上的那個 this.nodes.forEach(n => { if(node !== n && this.checkHit(node, n)){  const value = node.y - n.y;  offsetUpY = value > offsetUpY ? value : offsetUpY; } }); // 下移節點 this.nodes.forEach(n => { if(node !== n && n.y + n.h > node.y) {  n.y += (node.h + offsetUpY); } });}

offsetUpY 最終存放的是與拖拽節點發生碰撞的所有節點中,位置最靠上的節點與拖拽節點之間的距離。然后再下移過程中會加上該offsetUpY值,確保所有節點下移到拖拽節點下方。

這個插件的核心邏輯就說到這里了,讀者可以自己解決如下一些問題:

  1. 縮放限制,達到最小寬度就不能再繼續縮放了。
  2. 拖拽控制滾動條。
  3. 拖拽邊界的限制。
  4. 向下拖拽,達到碰撞節點1/2高度就發生換位。

總結

以上所述是小編給大家介紹的使用vue實現grid-layout功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美亚洲成人精品| 欧美一区二区三区艳史| 国产精品91视频| 91网站在线免费观看| 日韩精品中文字幕有码专区| 亚洲第一天堂无码专区| 国产精品久久久久9999| 亚洲欧美在线免费观看| 欧美日本啪啪无遮挡网站| 亚洲第一网站男人都懂| 日韩在线欧美在线| 久久精品视频导航| 国产精品成久久久久三级| 夜夜躁日日躁狠狠久久88av| 日韩中文字幕亚洲| 精品女厕一区二区三区| 欧美亚洲国产成人精品| 国产欧美一区二区三区久久人妖| 亚洲国产欧美一区| 亚洲男人天堂2019| 国产日韩欧美在线| 亚洲第一福利网| 伊人伊成久久人综合网站| 国产成人午夜视频网址| 久久在线精品视频| 久久精品一区中文字幕| 欧美日韩亚洲精品一区二区三区| 在线免费观看羞羞视频一区二区| 亚洲石原莉奈一区二区在线观看| 亚洲精品电影网| 亚洲网址你懂得| 午夜精品一区二区三区在线视频| 国产日韩一区在线| 91久久精品国产91性色| 国产精自产拍久久久久久| 国产精品影院在线观看| 国产亚洲精品激情久久| 亚洲乱码一区av黑人高潮| 高清一区二区三区四区五区| 久久国产天堂福利天堂| 91久久精品国产91久久性色| 2021久久精品国产99国产精品| 亚洲一区二区三区777| 精品亚洲精品福利线在观看| 亚洲一级黄色片| 日韩av在线免费观看一区| 成人h视频在线观看播放| 亚洲一区二区精品| 久久精品国产一区二区电影| 国产精品一区二区在线| 色老头一区二区三区在线观看| 国产成人精品一区二区| 国产日韩欧美在线视频观看| 欧美亚洲第一页| 欧美洲成人男女午夜视频| 成人妇女免费播放久久久| 91手机视频在线观看| 国产成人avxxxxx在线看| 欧美丰满少妇xxxxx| 欧美激情一区二区三区高清视频| 亚洲亚裔videos黑人hd| 精品视频久久久| 亚洲欧洲高清在线| 欧美日韩中文在线| 午夜精品蜜臀一区二区三区免费| 国产精品女主播视频| 亚洲成色999久久网站| 日韩有码在线视频| 国产精品亚洲激情| 亚洲免费一级电影| 26uuu另类亚洲欧美日本一| 色综合久久中文字幕综合网小说| 美女视频黄免费的亚洲男人天堂| 久久亚洲精品中文字幕冲田杏梨| 亚洲性生活视频| 国产亚洲视频中文字幕视频| 成人国产精品久久久久久亚洲| 亚洲精品福利免费在线观看| 色老头一区二区三区在线观看| 8090成年在线看片午夜| 日韩美女写真福利在线观看| 中文在线资源观看视频网站免费不卡| 国产视频在线一区二区| 国产99久久精品一区二区| 午夜欧美不卡精品aaaaa| 国产精品欧美一区二区| 国产成人自拍视频在线观看| 91精品啪aⅴ在线观看国产| 日韩中文字幕国产精品| 亚洲美女久久久| 欧美日韩国产区| 亚洲福利视频专区| 国产欧美最新羞羞视频在线观看| 成人国产在线视频| 91精品久久久久久综合乱菊| 中文字幕精品网| 亚洲国产精品字幕| 精品中文字幕视频| 91精品91久久久久久| 日韩中文字幕第一页| 国模叶桐国产精品一区| 国产精品久久久久7777婷婷| 97在线观看免费| 亚洲男女自偷自拍图片另类| 日本欧美一级片| 92看片淫黄大片欧美看国产片| 欧美日韩一区二区在线播放| 日韩美女免费线视频| 九九热精品在线| 国产精品大片wwwwww| 国产欧美日韩免费看aⅴ视频| 久久久久久久久久久成人| 国产精品视频自拍| 国产一区av在线| 久久夜色精品国产| 国产日韩欧美日韩| 国产精品久久久久福利| 国产一区二区三区三区在线观看| 中文字幕日韩欧美在线| 国产精品电影久久久久电影网| 中文字幕久热精品视频在线| 欧美电影在线免费观看网站| 成人在线免费观看视视频| 久久视频国产精品免费视频在线| 91av福利视频| 欧美精品久久久久久久久| 欧美激情图片区| 国产精品普通话| 日本成人激情视频| 欧美国产欧美亚洲国产日韩mv天天看完整| 中文字幕av一区二区| 国产97免费视| 亚洲欧美日韩第一区| 国产免费成人av| 国产一区二区香蕉| 5566日本婷婷色中文字幕97| www.久久撸.com| 欧美成人第一页| 国产精品日韩专区| 日韩免费av在线| 日韩av在线直播| 久久天天躁狠狠躁夜夜躁| 国产suv精品一区二区| www.欧美三级电影.com| 欧美性高潮在线| 国产91精品高潮白浆喷水| 亚洲一区二区三区777| 色偷偷av亚洲男人的天堂| 亚洲男人天堂网站| 欧美日韩亚洲一区二区三区| 九九热这里只有在线精品视| 欧美高跟鞋交xxxxxhd| 亚洲xxxx在线| 欧美午夜视频在线观看| 欧美激情视频三区| 国产精品视频免费在线观看| 日韩视频欧美视频| 亚洲人成电影在线播放| 欧美国产极速在线| 成人a视频在线观看| 日韩中文字幕在线免费观看| 91手机视频在线观看| 欧美一级免费视频| 国产精品日韩在线播放|