検索
ホームページウェブフロントエンドjsチュートリアルvueを使ったグリッドレイアウト機能の実装方法

この記事では主に vue を使用してグリッド レイアウト機能を実装するコードの説明を紹介します。必要な方は参照してください。

1. まず、プロジェクトをローカルにクローンします。

2. git restart --hard commit コマンドは、現在のヘッドが特定のコミットを指すようにすることができます。 git reset --hard commit 命令可以使当前head指向某个commit。

完成html的基本布局

点击复制按钮来复制整个commit id。然后在项目根路径下运行 git reset

HTMLの基本レイアウトを完成させます

コピーボタンをクリックしてコミットID全体をコピーします。次に、プロジェクトのルート パスで git restart を実行します。ブラウザで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 構成ファイルを確認するだけで環境を構築できます。

もう 1 つは、ノードのレイアウトです。各ノードは、横座標 (x) と縦座標 (y) によってノードの位置を制御できます。 (0, 0); ノードのサイズは幅 (w) と高さ (h) によって制御されます。各ノードには一意の ID も必要です。このように、ノードノードのデータ構造は次のようになります。

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

w と h の値は、グリッド内のセルの数です。たとえば、コンテナーは 24 セルで、幅は 960px です。各セルの幅が 40 ピクセルの場合、上記のノードは 240 ピクセル * 320 ピクセルとしてコンテナーの左上隅にレンダリングされます。

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 は各グリッドの幅と高さであるため、ノードの幅、高さ、変位を簡単に計算できます。

単一ノードのドラッグを完​​了する

ドラッグイベント

1. ドラッグを実装するには、mousedown、mousemove、mouseupを使用します。

2. これらのイベントはドキュメントにバインドされており、バインドする必要があるのは 1 回だけです。

実行プロセスは大まかに次のとおりです:

マウスがドラッグハンドル上で押されると、onMouseDownメソッドがトリガーされ、eventHandlerにいくつかの値が格納された後、マウスの動きがonMouseMoveメソッドをトリガーします。初回は、eventHandler.drag が false で、そのうち isDrag メソッドは、変位 (水平方向または垂直方向に 5 ピクセル移動) に基づいてドラッグ動作であるかどうかを判断します。ドラッグ動作である場合は、drag 属性を true に設定して実行します。同時に、dragdrop.dragStart メソッドを実行すると (ドラッグ動作は 1 回だけ実行されます)、マウスは動き続け、dragdrop.drag メソッドの実行が開始されます。最後に、マウスが放された後、onMouseUp メソッドが実行されて一部の状態が初期状態にリセットされ、同時に dragdrop.dragEnd メソッドが実行されます。

ノードのドラッグ アンド ドロップ

ノードのドラッグ アンド ドロップのロジックは、dragdrop.js ファイルにカプセル化されています。主なメソッドは、dragStart、drag、dragEnd です。

dragStart

ドラッグ動作では、このメソッドは 1 回だけ実行されるため、初期化作業を行うのに適しています。

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 パラメーターはドラッグ ハンドルです。要素、offsetX はマウスのドラッグ距離です。ドラッグ ハンドルの水平オフセット、offsetY はドラッグ ハンドルからのマウスの垂直オフセットです。

2. el を通じて、ドラッグ ノード (dragNode) とドラッグ コンテナー (dragContainer) を再帰的に見つけることができます。

3. ドラッグドロップ要素は、実際にマウスで制御されるドラッグ ノードであり、対応するレイアウト ノードは、シャドウ効果として視覚的に表示されるプレースホルダー ノードになります。

4. ドラッグ ノードを設定すると、実際にはクリックされたドラッグノードの innerHTML がドラッグドロップに設定され、スタイルも適用されます。

5. ドラッグインスタンスは実際にはdragrid.vueインスタンスであり、作成されたフック関数のキャッシュにそのインスタンスをキャッシュします。これにより、メソッドを呼び出すことができます。インスタンスで。

6.instance.current =ragNodeId; 設定後、ドラッグドロップノードとプレースホルダーノードのスタイルが適用されます。

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 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
JavaScriptエンジン:実装の比較JavaScriptエンジン:実装の比較Apr 13, 2025 am 12:05 AM

さまざまなJavaScriptエンジンは、各エンジンの実装原則と最適化戦略が異なるため、JavaScriptコードを解析および実行するときに異なる効果をもたらします。 1。語彙分析:ソースコードを語彙ユニットに変換します。 2。文法分析:抽象的な構文ツリーを生成します。 3。最適化とコンパイル:JITコンパイラを介してマシンコードを生成します。 4。実行:マシンコードを実行します。 V8エンジンはインスタントコンピレーションと非表示クラスを通じて最適化され、Spidermonkeyはタイプ推論システムを使用して、同じコードで異なるパフォーマンスパフォーマンスをもたらします。

ブラウザを超えて:現実世界のJavaScriptブラウザを超えて:現実世界のJavaScriptApr 12, 2025 am 12:06 AM

現実世界におけるJavaScriptのアプリケーションには、サーバー側のプログラミング、モバイルアプリケーション開発、モノのインターネット制御が含まれます。 2。モバイルアプリケーションの開発は、ReactNativeを通じて実行され、クロスプラットフォームの展開をサポートします。 3.ハードウェアの相互作用に適したJohnny-Fiveライブラリを介したIoTデバイス制御に使用されます。

next.jsを使用してマルチテナントSaaSアプリケーションを構築する(バックエンド統合)next.jsを使用してマルチテナントSaaSアプリケーションを構築する(バックエンド統合)Apr 11, 2025 am 08:23 AM

私はあなたの日常的な技術ツールを使用して機能的なマルチテナントSaaSアプリケーション(EDTECHアプリ)を作成しましたが、あなたは同じことをすることができます。 まず、マルチテナントSaaSアプリケーションとは何ですか? マルチテナントSaaSアプリケーションを使用すると、Singの複数の顧客にサービスを提供できます

next.jsを使用してマルチテナントSaaSアプリケーションを構築する方法(フロントエンド統合)next.jsを使用してマルチテナントSaaSアプリケーションを構築する方法(フロントエンド統合)Apr 11, 2025 am 08:22 AM

この記事では、許可によって保護されたバックエンドとのフロントエンド統合を示し、next.jsを使用して機能的なedtech SaaSアプリケーションを構築します。 FrontEndはユーザーのアクセス許可を取得してUIの可視性を制御し、APIリクエストがロールベースに付着することを保証します

JavaScript:Web言語の汎用性の調査JavaScript:Web言語の汎用性の調査Apr 11, 2025 am 12:01 AM

JavaScriptは、現代のWeb開発のコア言語であり、その多様性と柔軟性に広く使用されています。 1)フロントエンド開発:DOM操作と最新のフレームワーク(React、Vue.JS、Angularなど)を通じて、動的なWebページとシングルページアプリケーションを構築します。 2)サーバー側の開発:node.jsは、非ブロッキングI/Oモデルを使用して、高い並行性とリアルタイムアプリケーションを処理します。 3)モバイルおよびデスクトップアプリケーション開発:クロスプラットフォーム開発は、反応および電子を通じて実現され、開発効率を向上させます。

JavaScriptの進化:現在の傾向と将来の見通しJavaScriptの進化:現在の傾向と将来の見通しApr 10, 2025 am 09:33 AM

JavaScriptの最新トレンドには、TypeScriptの台頭、最新のフレームワークとライブラリの人気、WebAssemblyの適用が含まれます。将来の見通しは、より強力なタイプシステム、サーバー側のJavaScriptの開発、人工知能と機械学習の拡大、およびIoTおよびEDGEコンピューティングの可能性をカバーしています。

javascriptの分解:それが何をするのか、なぜそれが重要なのかjavascriptの分解:それが何をするのか、なぜそれが重要なのかApr 09, 2025 am 12:07 AM

JavaScriptは現代のWeb開発の基礎であり、その主な機能には、イベント駆動型のプログラミング、動的コンテンツ生成、非同期プログラミングが含まれます。 1)イベント駆動型プログラミングにより、Webページはユーザー操作に応じて動的に変更できます。 2)動的コンテンツ生成により、条件に応じてページコンテンツを調整できます。 3)非同期プログラミングにより、ユーザーインターフェイスがブロックされないようにします。 JavaScriptは、Webインタラクション、シングルページアプリケーション、サーバー側の開発で広く使用されており、ユーザーエクスペリエンスとクロスプラットフォーム開発の柔軟性を大幅に改善しています。

pythonまたはjavascriptの方がいいですか?pythonまたはjavascriptの方がいいですか?Apr 06, 2025 am 12:14 AM

Pythonはデータサイエンスや機械学習により適していますが、JavaScriptはフロントエンドとフルスタックの開発により適しています。 1. Pythonは、簡潔な構文とリッチライブラリエコシステムで知られており、データ分析とWeb開発に適しています。 2。JavaScriptは、フロントエンド開発の中核です。 node.jsはサーバー側のプログラミングをサポートしており、フルスタック開発に適しています。

See all articles

ホット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ヘンタイを無料で生成します。

ホットツール

mPDF

mPDF

mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

WebStorm Mac版

WebStorm Mac版

便利なJavaScript開発ツール

VSCode Windows 64 ビットのダウンロード

VSCode Windows 64 ビットのダウンロード

Microsoft によって発売された無料で強力な IDE エディター

EditPlus 中国語クラック版

EditPlus 中国語クラック版

サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

MantisBT

MantisBT

Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。