Vue實現類似於word插入表格時選中行列的效果

首先看一下效果:

在這裏插入圖片描述

實現的思路:
  1. 先設置一個容器的,容器中放上10×10的小格子,同時監聽容器的進入和離開方法。
  2. 每個小格子上設置鼠標進入的方法,同時傳入當前的序號,計算出當前的行和列,改變背景顏色。
  3. 監聽容器的是從那個位置進入,只有從左邊和上邊進入有效。
相關的核心代碼:
  • 判斷鼠標移入元素的方向——上下左右,核心代碼:
var direction = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180) / 90) + 3) % 4;

相關解釋參考這篇文章:

https://www.cnblogs.com/liuyanxia/p/5666892.html

相關代碼:

// 判斷鼠標是否從左上進入
direct(e) {
    const x = e.offsetX - 230 / 2; //鼠標進入的X座標-盒子寬度的一半
    const y = e.offsetY - 230 / 2; //鼠標進入的y座標-盒子寬度的一半
    const direction =
          (Math.round((Math.atan2(y, x) * (180 / Math.PI) + 180) / 90) + 3) % 4;
    if (direction === 0 || direction === 3) {
        this.isLeftTop = true;
    } else {
        this.isLeftTop = false;
    }
}
  • 判斷當前滑入到那個小窗格,改變背景顏色
// 獲取鼠標進入方格的位置,進行背景渲染
getMousePlace(index) {
    this.isOn = true;
    if (!this.isLeftTop || this.isSelected) {
        return;
    }
    const x = Math.floor(index % 10);
    const y = Math.floor(index / 10);
    const children = this.$refs.wallRow.children;
    for (let i = 0; i < this.divList.length; i++) {
        const childrenx = Math.floor(i % 10);
        const childreny = Math.floor(i / 10);
        if (childrenx < x + 1 && childreny < y + 1) {
            children[i].className = "bgColor";
            this.rows = x + 1;
            this.cols = y + 1;
        } else {
            children[i].className = "";
        }
    }
}
  • 點擊時關閉繪製狀態
// 選定行列
selected(index) {
    this.isSelected = true;
    const data = {
        rows: this.rows,
        cols: this.cols
    };
}
整體代碼如下:
<template>
  <div class="custom-style">
    <div class="word">
      <span>{{ rows }}×{{ cols }}</span>
    </div>
    <ul class="wall-row" ref="wallRow" @mouseenter="direct($event)" @mouseleave="clearSelect">
      <li
        v-for="(item, index) in divList"
        :key="index"
        @mouseenter="getMousePlace(index)"
        @click="selected"
      ></li>
    </ul>
    <div class="wall-cancel">
      <button @click="cancel">重置</button>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    isBlur: {
      type: Boolean
    }
  },
  data() {
    return {
      divList: [], // 方格列表數組
      rows: 1, // 行
      cols: 1, // 列
      isLeftTop: true, // 鼠標是否從左上方進入
      isSelected: false, // 是否選中電視牆行列
      isOn: false
    };
  },
  created() {
    this.divList.length = 100; // 方格個數 10*10
  },
  mounted() {
    this.autoSelect();
  },
  methods: {
    // 自動根據vuex中行列選中對應方格
    autoSelect() {
      this.rows = 1;
      this.cols = 1;
      this.selectRowCol(this.rows, this.cols);
    },
    // 獲取鼠標進入方格的位置,進行背景渲染
    getMousePlace(index) {
      this.isOn = true;
      if (!this.isLeftTop || this.isSelected) {
        return;
      }
      const x = Math.floor(index % 10);
      const y = Math.floor(index / 10);
      const children = this.$refs.wallRow.children;
      for (let i = 0; i < this.divList.length; i++) {
        const childrenx = Math.floor(i % 10);
        const childreny = Math.floor(i / 10);
        if (childrenx < x + 1 && childreny < y + 1) {
          children[i].className = "bgColor";
          this.rows = x + 1;
          this.cols = y + 1;
        } else {
          children[i].className = "";
        }
      }
    },
    // 當鼠標移出選擇區域,如未選定行列,則重置方格 1*1
    clearSelect() {
      this.isOn = false;
      if (this.isSelected) {
        return;
      }
      const children = this.$refs.wallRow.children;
      for (let i = 0; i < this.divList.length; i++) {
        if (i != 0) {
          children[i].className = "";
        }
      }
      this.rows = 1;
      this.cols = 1;
    },
    // 選定行列
    selected(index) {
      this.isSelected = true;
      const data = {
        rows: this.rows,
        cols: this.cols
      };
    },
    // 判斷鼠標是否從左上進入
    direct(e) {
      const x = e.offsetX - 230 / 2; //鼠標進入的X座標-盒子寬度的一半
      const y = e.offsetY - 230 / 2; //鼠標進入的y座標-盒子寬度的一半
      const direction =
        (Math.round((Math.atan2(y, x) * (180 / Math.PI) + 180) / 90) + 3) % 4;
      if (direction === 0) {
        this.isLeftTop = true;
      } else if (direction === 1) {
        this.isLeftTop = false;
      } else if (direction === 2) {
        this.isLeftTop = false;
      } else {
        this.isLeftTop = true;
      }
    },
    // 點擊取消,重置方格 1*1
    cancel() {
      const children = this.$refs.wallRow.children;
      for (let i = 0; i < this.divList.length; i++) {
        if (i != 0) {
          children[i].className = "";
        }
      }
      this.rows = 1;
      this.cols = 1;
      this.isSelected = false;
    },
    // 改變行列數
    handleTypeChange() {
      this.selectRowCol(this.rows, this.cols);
      this.isSelected = true;
    },
    // 渲染方格
    selectRowCol(rows, cols) {
      const children = this.$refs.wallRow.children;
      for (let i = 0; i < this.divList.length; i++) {
        const childrenx = Math.floor(i % 10);
        const childreny = Math.floor(i / 10);
        if (childrenx < rows && childreny < cols) {
          children[i].className = "bgColor";
        } else {
          children[i].className = "";
        }
      }
    },
    // 點擊保存,將數據同步到vuex,並關閉下拉框
    save() {
      const data = {
        rows: this.rows,
        cols: this.cols
      };
    }
  }
};
</script>

<style scoped lang="css">
.custom-style {
  position: absolute;
  margin: 0;
  padding: 0;
  height: 440px;
  width: 400px;
  left: 50%;
  top: 50%;
  margin-left: -200px;
  margin-top: -210px;
}

.word {
  display: block;
  width: 100%;
  height: 20px;
  font-size: 13px;
  background-color: #e2eef6;
}
.word span {
  line-height: 20px;
  text-align: center;
}

.wall-row {
  width: 400px;
  height: 400px;
  padding: 0;
  margin: 0;
  display: block;
  position: relative;
}

.wall-row li {
  list-style: none;
  width: 39px;
  height: 40px;
  border: 0.5px solid #fff;
  float: left;
  background-color: #ebf0f4;
}

.wall-row .bgColor {
  background-color: #00b4ff;
}

.word-cancel {
  width: 100%;
  height: 20px;
  display: flex;
  justify-content: center;
  align-items: center;
}

</style>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章