이 글은 주로 vue를 사용하여 그리드 레이아웃 기능을 구현하는 코드 설명을 소개합니다. 필요한 친구가 참고할 수 있습니다.
1. 먼저 프로젝트를 로컬로 복제합니다.
2. git Reset --hard commit
명령은 현재 헤드가 특정 커밋을 가리키도록 할 수 있습니다. git reset --hard commit
命令可以使当前head指向某个commit。
完成html的基本布局
点击复制按钮来复制整个commit id。然后在项目根路径下运行 git reset
html의 기본 레이아웃을 완성합니다
복사 버튼을 클릭하면 전체 커밋 ID가 복사됩니다. 그런 다음 프로젝트 루트 경로에서git Reset
을 실행하세요. 효과를 미리 보려면 브라우저로 index.html을 엽니다. 플러그인의 주요 HTML 결과는 다음과 같습니다. <!-- 节点容器 --> <p class="dragrid"> <!-- 可拖拽的节点,使用translate控制位移 --> <p class="dragrid-item" style="transform: translate(0px, 0px)"> <!-- 通过slot可以插入动态内容 --> <p class="dragrid-item-content"> </p> <!-- 拖拽句柄 --> <p class="dragrid-drag-bar"></p> <!-- 缩放句柄 --> <p class="dragrid-resize-bar"></p> </p> </p>
vue를 사용하여 노드의 간단한 레이아웃 완성
먼저 커밋으로 전환하고 필요한 패키지를 설치합니다. 그리고 다음 명령을 실행하세요:git reset --hard 83842ea107e7d819761f25bf06bfc545102b2944 npm install <!-- 启动,端口为7777,在package.json中可以修改 --> npm start
{ id: "uuid", x: 0, y: 0, w: 6, h: 8 }
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)" }; } }
단일 노드 전체 드래그
Drag 이벤트
1. mousedown, mousemove, mouseup을 사용하여 드래그를 구현합니다. 2. 이러한 이벤트는 문서에 바인딩되며 한 번만 바인딩하면 됩니다.실행 과정은 대략 다음과 같습니다.
드래그 핸들을 마우스로 누르면 onMouseDown 메서드가 트리거됩니다. eventHandler에 일부 값을 저장한 후 마우스 움직임에 대해 onMouseMove 메서드가 트리거됩니다. 처음에는 eventHandler.drag가 false이고, 그 중 isDrag 메소드는 변위(수평 또는 수직으로 5픽셀 이동)를 기준으로 드래그 동작인지 여부를 결정합니다. 드래그 동작인 경우 드래그 속성을 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; }
6.instance.current = dragNodeId; 설정 후 드래그드롭 노드와 플레이스홀더 노드의 스타일이 적용됩니다.
7. 캐시된 데이터의 offsetX 및 offsetY는 노드의 왼쪽 위 모서리를 기준으로 한 드래그 핸들의 오프셋입니다.drag
드래그 동작이 발생한 후 마우스 이동이 이 메서드를 실행하고 드래그된 노드의 스타일을 지속적으로 업데이트하여 노드가 이동됩니다.
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
는 주로 상태를 재설정하는 데 사용됩니다. 논리는 비교적 간단하므로 자세히 설명하지 않겠습니다.
이때 마우스를 따라 단일 노드를 이동할 수 있습니다.드래그된 노드의 움직임을 따르도록 플레이스홀더 활성화
이 섹션은 드래그된 노드와 함께 움직이는 플레이스홀더 노드(플레이스홀더의 그림자 부분)에 관한 것입니다. 주요 아이디어는 다음과 같습니다. 컨테이너에서 노드의 오프셋(드래그 방법의 x, y)을 드래그하여 해당 그리드의 좌표로 변환할 수 있습니다.변환된 좌표가 변경되면 자리 표시자 노드의 좌표가 업데이트됩니다.
드래그 메소드에 추가된 코드는 다음과 같습니다.// 坐标转换 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; }
노드 재배열 및 위로 이동
이 섹션의 핵심 포인트는 두 가지입니다.2차원 배열을 사용하여 그리드를 표현합니다. 이 2차원 배열에는 노드의 위치 정보가 표시될 수 있습니다.
노드에서 노드가 변경되는 한 재배열되어야 하며 각 노드는 최대한 위로 이동되어야 합니다. 🎜🎜🎜2차원 배열 구축🎜🎜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高度就发生换位。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
vue+springboot如何实现单点登录跨域问题(详细教程)
위 내용은 vue를 사용하여 그리드 레이아웃 기능을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!