웹 프론트엔드 JS 튜토리얼 vue를 사용하여 그리드 레이아웃 기능을 구현하는 방법

vue를 사용하여 그리드 레이아웃 기능을 구현하는 방법

Jun 13, 2018 pm 03:52 PM
grid vue

이 글은 주로 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
로그인 후 복사

이 단계 1은 환경을 구축하는 것입니다. 이를 위해 webpack.config.js 구성 파일을 살펴보세요.

다른 하나는 노드의 레이아웃입니다. 주요 아이디어는 노드 컨테이너를 그리드로 간주하는 것입니다. 각 노드는 가로좌표(x)와 세로좌표(y)를 통해 노드의 위치를 ​​제어할 수 있습니다. (0, 0); 노드 크기는 너비(w)와 높이(h)에 의해 제어됩니다. 각 노드에는 고유한 ID도 있어야 합니다. 이런 방식으로 노드 노드의 데이터 구조는 다음과 같습니다.

{
 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 + &#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)"
 };
 }
}
로그인 후 복사

여기서 cellW와 cellH는 각 그리드의 너비와 높이이므로 노드의 너비, 높이 및 변위를 쉽게 계산할 수 있습니다.

단일 노드 전체 드래그

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, &#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 매개변수 el은 드래그 핸들입니다. 요소, offsetX는 마우스 거리 드래그입니다. 드래그 핸들의 수평 오프셋이고, offsetY는 드래그 핸들에서 마우스의 수직 오프셋입니다.

2.el을 통해 드래그 노드(dragNode)와 드래그 컨테이너(dragContainer)를 재귀적으로 찾을 수 있습니다.

3. 드래그드롭 요소는 실제 마우스로 제어되는 드래그 노드이며 해당 레이아웃 노드는 그림자 효과로 시각적으로 표시되는 자리 표시자 노드가 됩니다.

4. 드래그 노드를 설정하면 실제로 클릭한 dragNode의 innerHTML이 드래그 드롭으로 설정되고 스타일도 적용됩니다.

5. 드래그 인스턴스는 실제로 dragrid.vue 인스턴스입니다. 생성된 후크 함수의 캐시에 해당 인스턴스를 캐시하여 이름에 따라 인스턴스를 가져올 수 있습니다. .

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

주로 컨테이너를 기준으로 노드의 오프셋을 계산합니다. 마우스와 페이지 사이의 거리 - 컨테이너의 오프셋 - 마우스와 노드 사이의 거리 노드와 컨테이너 사이의 거리입니다. 노드와 컨테이너 사이.

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 += &#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如何实现单点登录跨域问题(详细教程)

위 내용은 vue를 사용하여 그리드 레이아웃 기능을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
4 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
4 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
4 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 채팅 명령 및 사용 방법
4 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Vue 용 버튼에 기능을 추가하는 방법 Vue 용 버튼에 기능을 추가하는 방법 Apr 08, 2025 am 08:51 AM

HTML 템플릿의 버튼을 메소드에 바인딩하여 VUE 버튼에 함수를 추가 할 수 있습니다. 메소드를 정의하고 VUE 인스턴스에서 기능 로직을 작성하십시오.

Vue에서 부트 스트랩을 사용하는 방법 Vue에서 부트 스트랩을 사용하는 방법 Apr 07, 2025 pm 11:33 PM

vue.js에서 bootstrap 사용은 5 단계로 나뉩니다 : Bootstrap 설치. main.js.의 부트 스트랩 가져 오기 부트 스트랩 구성 요소를 템플릿에서 직접 사용하십시오. 선택 사항 : 사용자 정의 스타일. 선택 사항 : 플러그인을 사용하십시오.

vue.js로 JS 파일을 참조하는 방법 vue.js로 JS 파일을 참조하는 방법 Apr 07, 2025 pm 11:27 PM

vue.js에서 JS 파일을 참조하는 세 가지 방법이 있습니다. & lt; script & gt; 꼬리표;; mounted () 라이프 사이클 후크를 사용한 동적 가져 오기; Vuex State Management Library를 통해 수입.

Vue에서 시계를 사용하는 방법 Vue에서 시계를 사용하는 방법 Apr 07, 2025 pm 11:36 PM

vue.js의 시계 옵션을 사용하면 개발자가 특정 데이터의 변경 사항을들을 수 있습니다. 데이터가 변경되면 콜백 기능을 트리거하여 업데이트보기 또는 기타 작업을 수행합니다. 구성 옵션에는 즉시 콜백을 실행할지 여부와 DEEP를 지정하는 즉시 포함되며, 이는 객체 또는 어레이에 대한 변경 사항을 재귀 적으로 듣는 지 여부를 지정합니다.

Vue가 이전 페이지로 돌아 오는 방법 Vue가 이전 페이지로 돌아 오는 방법 Apr 07, 2025 pm 11:30 PM

vue.js는 이전 페이지로 돌아갈 수있는 네 가지 방법이 있습니다. $ router.go (-1) $ router.back () 사용 & lt; router-link to = & quot;/quot; Component Window.history.back () 및 메소드 선택은 장면에 따라 다릅니다.

Vue는 천막/텍스트 스크롤 효과를 인식합니다 Vue는 천막/텍스트 스크롤 효과를 인식합니다 Apr 07, 2025 pm 10:51 PM

CSS 애니메이션 또는 타사 라이브러리를 사용하여 VUE에서 Marquee/Text Scrolling Effects를 구현하십시오. 이 기사는 CSS 애니메이션 사용 방법을 소개합니다. & lt; div & gt; CSS 애니메이션을 정의하고 오버플로를 설정하십시오 : 숨겨진, 너비 및 애니메이션. 키 프레임을 정의하고 변환을 설정하십시오 : Translatex () 애니메이션의 시작과 끝에서. 지속 시간, 스크롤 속도 및 방향과 같은 애니메이션 속성을 조정하십시오.

Vue Pagination 사용 방법 Vue Pagination 사용 방법 Apr 08, 2025 am 06:45 AM

Pagination은 큰 데이터 세트를 작은 페이지로 나누어 성능 및 사용자 경험을 향상시키는 기술입니다. VUE에서 다음 내장 방법을 페이징에 사용할 수 있습니다. 총 페이지 수를 계산하십시오 : TotalPages () Traversal 페이지 번호 : V-For Directive 현재 페이지를 설정하려면 : CurrentPage 현재 페이지 데이터 가져 오기 : currentPagedAta ()

함수 인터셉트 vue를 사용하는 방법 함수 인터셉트 vue를 사용하는 방법 Apr 08, 2025 am 06:51 AM

VUE의 기능 차단은 지정된 기간 내에 기능이 호출되는 횟수를 제한하고 성능 문제를 방지하는 데 사용되는 기술입니다. 구현 방법은 다음과 같습니다. lodash 라이브러리 가져 오기 : 'lodash'에서 import {debounce}; Debounce 기능을 사용하여 인터셉트 기능을 만듭니다. const debouncedfunction = debounce (() = & gt; { / logical /}, 500); 인터셉트 함수를 호출하면 제어 기능이 최대 500 밀리 초 안에 한 번 호출됩니다.

See all articles