Grid layout + drag and drop
Recently, the project team of the company felt that the customized functions of Ali Cloud or Huawei cloud console were very good, so THEY asked me to do some research. As a rookie, I could only do a little research.
A small rearranged or replaceable demo was implemented as a base for the time being, and further development will follow.
The main one is the grid layout, where each cell is currently defined as grid-template-Areas. Therefore, the current function is not perfect, it is a basic small demo.
Without componentization, the complete code on one page looks like this:
<template >
<div>
<div
ref="gridContent"
class="grid_group"
:style="{ 'grid-template-areas': gridAreas }"
>
<div
v-for="(dragData, index) in dragList"
:key="index"
v-drag
:style="{ 'grid-area': 'area-' + index }"
class="drag-item"
onselectstart="return false;"
>
<div class="drag-data-div">This is {{dragData}} data</div>
</div>
</div>
</div>
</template>
<script>
import _ from "lodash";
export default {
name: "mapStudy".directives: {
drag: {
bind: function (el, binding, vnode) {
const moveEl = el;
moveEl.onmousedown = (event) = > {
moveEl.style.boxShadow = "#e6e6e6 0 0 10px 10px";
moveEl.style.zIndex = 100;
vnode.context.dragStart(event);
// Gets the current position of the clicked element
const disX = event.clientX;
const disY = event.clientY;
document.onmousemove = (dEvent) = > {
// Get the distance moved
let x = dEvent.clientX - disX;
let y = dEvent.clientY - disY;
// Get the region where the current element can be moved
const { minX, maxX, minY, maxY } = vnode.context.getRangeOfEl(
moveEl
);
console.log(minX, maxX, minY, maxY);
x = x < minX ? minX : x > maxX ? maxX : x;
y = y < minY ? minY : y > maxY ? maxY : y;
moveEl.style.left = x + "px";
moveEl.style.top = y + "px";
};
document.onmouseup = (upEvent) = > {
document.onmousemove = null; // The event listener needs to be cancelled
document.onmouseup = null; // The event listener needs to be cancelled
moveEl.style.boxShadow = "none"; vnode.context.changeBlock(moveEl); vnode.context.dragEnd(upEvent, vnode.context.dragList); }; }; }},},data() {
return {
column: 3.gridAreas: "".dragDataList: ["1"."2"."3"."4"."5"."6"."Seven"."8"."9"].dragList: [].type: "To replace the replace".// rearrange resort/ replace
};
},
mounted: function () {
// Clone, otherwise resort assignment is a bit of a problem, you can also choose another way to optimize
this.dragList = _.cloneDeep(this.dragDataList);
this.joinGridArea();
},
methods: {
// Generate a grid layout area based on the data.
joinGridArea: function () {
console.log(this.dragList);
const len = this.dragList.length;
let areaStr = "";
for (let i = 0; i < len; i++) {
if (i % this.column === 0) {
areaStr += '"area-' + i + "";
if (this.column === 1) {
areaStr += '"'; }}else if (i % this.column === this.column - 1) {
areaStr += "area-" + i + '"';
} else {
areaStr += "area-" + i + ""; }}if (len % this.column ! = =0) {
const emptyLength = this.column - (len % this.column);
areaStr += new Array(emptyLength).fill(".").join("") + '"';
}
this.gridAreas = areaStr;
},
dragStart: function (event) {
this.$message({
type: "info".message: 'Drag starts, and you can view the event parameter through the console,The ${JSON.stringify(
event
)}`});console.info("Start dragging", event);
},
dragEnd: function (event, dragList) {
this.$message({
type: "info".message: 'After the drag, you can view the event parameters on the console.The ${JSON.stringify(
event
)}.${dragList}`});console.info("End of drag.", event, dragList);
},
// Rearrange or replace data at the end of the drag
changeBlock: function (moveEl) {
// Move the cube into the corresponding area
const { nowIndex, index } = this.getIndexOfMoveEL(moveEl);
if (this.type === "replace") {
const temp = this.dragList[index];
this.$set(this.dragList, index, this.dragList[nowIndex]);
this.$set(this.dragList, nowIndex, temp);
} else {
this.dragList.splice(index, 1);
this.dragList.splice(nowIndex, 0.this.dragDataList[index]);
}
moveEl.style.left = 0;
moveEl.style.top = 0;
},
// // calculates how far the current element can move
getRangeOfEl: function (moveEl) {
const index = parseInt(
moveEl.style.gridArea.split("/") [0].split("-") [1]);// Calculate the area where the selected block can move
const res = {};
// The number of columns where the current block is located.
const currentColummn = index % this.column;
// Left: -(width of the currently selected block + spacing)* position of the column in which the current block is located.
res.minX = -((moveEl.offsetWidth + 5) * currentColummn);
// right :(width of currently selected block + spacing)* position of the column in which the current block is located.
res.maxX = (this.column - currentColummn - 1) * (moveEl.offsetWidth + 5);
// Total number of columns, get the total number of rows
const allRow = Math.ceil(this.dragList.length / this.column);
// const allRow = Math.ceil(9 / this.column);
// The current block position/column number, determine the current block row.
const currentRow = Math.floor(index / this.column);
// top: -(height of the currently selected block + spacing)* position of the current block in the row.
res.minY = -((moveEl.offsetHeight + 5) * currentRow);
*(height of the currently selected block + spacing).
res.maxY = (allRow - currentRow - 1) * (moveEl.offsetHeight + 5);
return res;
},
getIndexOfMoveEL: function (moveEl) {
const x = parseInt(moveEl.style.left.split("px") [0]);
const y = parseInt(moveEl.style.top.split("px") [0]);
const index = parseInt(
moveEl.style.gridArea.split("/") [0].split("-") [1]);let nowIndex = 0;
if (x < 0) {
nowIndex = index - Math.round(Math.abs(x) / moveEl.offsetWidth);
} else {
nowIndex = index + Math.round(Math.abs(x) / moveEl.offsetWidth);
}
if (y < 0) {
nowIndex =
nowIndex -
Math.round(Math.abs(y) / moveEl.offsetHeight) * this.column;
} else {
nowIndex =
nowIndex +
Math.round(Math.abs(y) / moveEl.offsetHeight) * this.column;
}
console.log(nowIndex);
return{ nowIndex, index }; ,}}};</script>
<style lang="less" scoped>
.grid_group {
--columnWidth: "auto";
--rowHeight: "auto";
display: grid;
gap: 5px 5px;
justify-content: center;
align-content: center;
width: fit-content;
position: relative;
.drag-item {
position: relative;
width: var(--columnWidth);
height: var(--rowHeight);
line-height: var(--rowHeight);
text-align: center;
user-select: none;
.drag-data-div {
background-color: gray;
color: #ffffff;
width: 200px;
height: 200px;
line-height: 200px; }}}</style>
Copy the code
Reference article:
Vue.js encapsulates multi-column layout drag and drop (Grid Layout)