Maison  >  Article  >  interface Web  >  Comment implémenter la fonction de disposition en grille en utilisant vue

Comment implémenter la fonction de disposition en grille en utilisant vue

亚连
亚连original
2018-06-13 15:52:334054parcourir

Cet article présente principalement l'explication du code sur l'utilisation de vue pour implémenter la fonction de disposition en grille. Les amis dans le besoin peuvent s'y référer

1. Clonez d'abord le projet localement.

2. La commande git reset --hard commit peut faire pointer le head actuel vers un commit.

Complétez la mise en page de base du HTML

Cliquez sur le bouton Copier pour copier l'intégralité de l'identifiant de validation. Ensuite, exécutez git reset dans le chemin racine du projet. Ouvrez index.html avec un navigateur pour prévisualiser l'effet. Les principaux résultats HTML du plug-in sont les suivants :

<!-- 节点容器 -->
<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>

Utilisez vue pour compléter une disposition simple des nœuds

Changez d'abord de commit. Pour installer les packages requis, exécutez la commande suivante :

git reset --hard 83842ea107e7d819761f25bf06bfc545102b2944
npm install
<!-- 启动,端口为7777,在package.json中可以修改 -->
npm start

La première étape consiste à configurer l'environnement. Pour cela, regardez simplement le webpack.config.js. fichier de configuration.

L'autre est la disposition des nœuds. L'idée principale est de considérer le conteneur de nœuds comme une grille. Chaque nœud peut contrôler la position du nœud à travers l'abscisse (x) et l'ordonnée (y). la coordonnée du coin supérieur gauche est (0, 0) ; la taille du nœud est contrôlée par la largeur (w) et la hauteur (h) ; chaque nœud doit également avoir un identifiant unique. De cette façon, la structure des données du nœud nœud est :

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

Les valeurs​​de w et h sont le nombre de cellules dans la grille. Par exemple, le conteneur est de 24 cellules et. la largeur est de 960 px et la largeur de chaque cellule est de 40 px , le nœud ci-dessus est alors rendu sous la forme 240 px * 320 px et se trouve dans le coin supérieur gauche du conteneur.

Regardons la logique correspondante de 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 + &#39;px&#39;,
  height: node.h * this.cfg.cellH + &#39;px&#39;,
  transform: "translate("+ node.x * this.cfg.cellW +"px, "+ node.y * this.cfg.cellH +"px)"
 };
 }
}

où cellW et cellH sont la largeur et la hauteur de chaque grille, de sorte que la largeur, la hauteur et le déplacement de les nœuds sont calculés. C'est facile.

Glisser complet d'un seul nœud

Événement Drag

1 Utilisez mousedown, mousemove, mouseup pour implémenter le glisser-déposer. .

2. Ces événements sont liés au document et ne doivent être liés qu'une seule fois.

Le processus d'exécution est à peu près le suivant :

Lorsque la souris est enfoncée sur la poignée de déplacement, la méthode onMouseDown est déclenchée. Après avoir stocké certaines valeurs dans eventHandler, le mouvement de la souris se déclenche. la méthode onMouseMove. Lors de la première saisie de eventHandler.drag est fausse, et la méthode isDrag déterminera s'il s'agit d'un comportement de glissement basé sur le déplacement (déplacement de 5 pixels horizontalement ou verticalement). faites glisser l'attribut sur true et exécutez la méthode dragdrop.dragStart (une fois que le comportement de glissement ne sera exécuté qu'une seule fois), puis la souris continue de se déplacer et la méthode dragdrop.drag commencera à être exécutée. Enfin, une fois la souris relâchée, la méthode onMouseUp sera exécutée pour réinitialiser certains états à l'état initial, et la méthode dragdrop.dragEnd sera exécutée en même temps.

Drag Node

La logique de glisser des nœuds est encapsulée dans le fichier dragdrop.js. Les méthodes principales sont dragStart, drag et dragEnd.

dragStart

Dans un comportement de glissement, cette méthode n'est exécutée qu'une seule fois, elle convient donc pour effectuer un travail d'initialisation. À ce stade, le code est le suivant. :

dragStart(el, offsetX, offsetY) {
 // 要拖拽的节点
 const dragNode = utils.searchUp(el, &#39;dragrid-item&#39;);
 // 容器
 const dragContainer = utils.searchUp(el, &#39;dragrid&#39;);
 // 拖拽实例
 const instance = cache.get(dragContainer.getAttribute(&#39;name&#39;));
 // 拖拽节点
 const dragdrop = dragContainer.querySelector(&#39;.dragrid-dragdrop&#39;);
 // 拖拽节点id
 const dragNodeId = dragNode.getAttribute(&#39;dg-id&#39;);
 // 设置拖拽节点
 dragdrop.setAttribute(&#39;style&#39;, dragNode.getAttribute(&#39;style&#39;));
 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. Le paramètre el est l'élément de la poignée de déplacement, offsetX est le décalage horizontal de la souris par rapport à la poignée de déplacement et offsetY est le décalage vertical de la souris par rapport à la poignée de déplacement.

2. Grâce à el, vous pouvez trouver récursivement le nœud de déplacement (dragNode) et le conteneur de déplacement (dragContainer).

3. L'élément dragdrop est un véritable nœud de déplacement contrôlé par la souris, et le nœud de mise en page correspondant deviendra un nœud d'espace réservé, qui est affiché visuellement sous forme d'effet d'ombre.

4. La définition du nœud de déplacement définit en fait le innerHTML du dragNode cliqué sur le glisser-déposer et lui applique également le style.

5. L'instance drag est en fait l'instance dragrid.vue. Elle met son instance en cache dans la fonction hook créée. Ici, vous pouvez obtenir l'instance du cache en fonction de son nom. appelez la méthode dans l’exemple.

6.instance.current = dragNodeId; Après le réglage, les styles du nœud glisser-déposer et du nœud espace réservé sont appliqués.

7. Les offsetX et offsetY dans les données mises en cache sont les décalages de la poignée de déplacement par rapport au coin supérieur gauche du nœud.

glisser

Une fois le comportement de glissement effectué, le déplacement de la souris exécutera cette méthode et le nœud se déplacera en mettant constamment à jour le style du nœud déplacé.

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 += &#39;;transform:translate(&#39;+ x +&#39;px, &#39;+ y +&#39;px)&#39;;
}

Calcule principalement le décalage du nœud par rapport au conteneur : la distance entre la souris et la page - le décalage du conteneur - la distance entre la souris et le nœud La distance entre le nœud. et le conteneur est la distance entre le nœud et le conteneur.

dragEnd

Principalement réinitialiser l'état. La logique est relativement simple, je n’entrerai donc pas dans les détails.

À ce stade, un seul nœud peut être déplacé en suivant la souris.

Permettre à l'espace réservé de suivre le mouvement du nœud déplacé

Cette section concerne le nœud d'espace réservé (la partie ombre de l'espace réservé) se déplaçant avec le nœud déplacé . L'idée principale est :

En faisant glisser le décalage du nœud depuis le conteneur (x, y dans la méthode glisser), il peut être converti en coordonnées de la grille correspondante.

Si les coordonnées converties changent, mettez à jour les coordonnées du nœud d'espace réservé.

Le code ajouté dans la méthode glisser est le suivant :

// 坐标转换
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;
}

Réarrangement et déplacement des nœuds

Il y a deux points essentiels dans ce section :

Utilisez un tableau bidimensionnel pour représenter la grille, afin que les informations de localisation des nœuds puissent être marquées dans ce tableau bidimensionnel.

Tant qu'un nœud change de nœuds, il doit être réorganisé et chaque nœud doit être déplacé vers le haut autant que possible.

Construction d'un tableau bidimensionnel

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 += &#39;;width:&#39; + w + &#39;px;height:&#39; + h + &#39;px;&#39;;
 // 坐标转换
 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高度就发生换位。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在JavaScript中如何实现读取和写入cookie

在vue中scroller返回页面并且记住滚动位置如何实现

vue+springboot如何实现单点登录跨域问题(详细教程)

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn