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)