到这里已经单个节点已经可以跟随鼠标进行移动了。
使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。
到这里,拖拽节点移动时,占位节点会尽可能地上移,如果只有一个节点,那么占位节点一直在最上面移动。
相关节点的下移
拖拽节点移动时,与拖拽节点发生碰撞的节点及其下发的节点,都先下移一定距离,这样拖拽节点就可以移到相应位置,最后节点都会发生上一节所说的上移。