vue模塊移動組件

一直都想實現類似於五百丁中簡歷填寫中模塊跟隨鼠標移動的組件,最近閒來無事,自己琢磨實現了一個差不多的組件。

其中每個模塊都是組件調入(項目經驗、教育經驗、工作經驗等),所以這裏也用到了動態加載組件方式。
思路:鼠標移入模塊,顯示相應模塊的點擊移動按鈕,點擊A模塊移動按鈕,此時原先A模塊所在的位置上變爲拖動到這裏綠框模塊,同時鼠標下懸浮着A模塊,鼠標移動,懸浮的A模塊跟隨移動,綠框也跟隨上下移動。
父組件
1、父組件template中的代碼
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
<div class="component-box" ref="compBox">
  <component
      v-for="(item, index) in comRoute"
      :is="item"
      :key="index"
      @getData="getData">
</component>
  <div :class="['translate-box', {'move-icon':transType}]"
       ref="translateBox" v-if="transType">
    <component :is="transCom"></component>
  </div>
</div>
2、data中定義的屬性
[JavaScript] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
comList: ['educationExp', 'workExp', 'projectExp'], // 模塊列表
comLen: 0, // 模塊的長度
comType: '', // 當前移動的模塊
transType: '', // 當前移動的模塊
coordinate: { // 鼠標座標
  mouseX: 0,
  mouseY: 0,
},
downFlag: false, // 當前是否點擊模塊移動
mouseYBefore: 0, // 記錄鼠標點擊時Y座標以及鼠標每移動30後重新記錄鼠標Y座標
mouseYLast: 0, // 實時記錄鼠標移動時的Y座標
nowCom: '', // 移動模塊時,上一個模塊或者下一個模塊的名稱
forFlage: false, // forEach遍歷結束的標識
comRoute: [], // 動態加載組件列表
transCom: null, // 點擊後懸浮的組件
compBox: null, // 獲取當前組件在頁面中的位置信息
3、scrollTop爲頁面滾動的距頂部的距離,從父組件傳過來
[JavaScript] 純文本查看 複製代碼
1
props: {  scrollTop: Number,}
4、watch一些屬性
[JavaScript] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
watch: {
  comList: {
    handler(val) {
      this.getCom(val); // 模塊列表改變時,實時加載組件
    },
    deep: true,
    immediate: true, // 聲明瞭之後會立馬執行handler裏面的函數
  },
  transType(val) { // 懸浮模塊加載組件
    if (val) {
      this.transCom = () => import(`./default/${val}`);
    }
  },
  scrollTop: { // 監聽頁面滾動
    handler() {},
    immediate: true,
  },
  comType(newVal) {
    if (newVal) {
      this.comList.forEach((item, index) => {
        if (item === newVal) {
          this.comList[index] = 'moveBox'// 將組建列表中爲comType的元素改爲moveBox組件
        }
      });
      this.getCom(this.comList);
    }
  },
  downFlag(newVal) { // 鼠標已經點擊
    const nowThis = this;
    document.onmousemove = function (e) {
      if (newVal) { // 鼠標移動賦值
        nowThis.coordinate.mouseX = e.clientX;
        nowThis.coordinate.mouseY = e.clientY;
      }
    };
    document.onmouseup = function () { // 鼠標鬆開
      document.onmousemove = null;
      if (newVal) {
        nowThis.transType = ''; // 懸浮組件置空
        nowThis.comList.forEach((item, index) => {
          if (item === 'moveBox') { // 尋找moveBox所在位置
            nowThis.comList[index] = nowThis.comType; // 還原成點擊組件
          }
        });
        nowThis.downFlag = false;
        nowThis.comType = '';
        nowThis.getCom(nowThis.comList);
      }
    };
  },
  coordinate: {
    handler(newVal) { // 懸浮組件位置
      this.$refs.translateBox.style.top = `${newVal.mouseY + this.scrollTop - 40 - this.compBox.y}px`;
      this.$refs.translateBox.style.left = `${newVal.mouseX - this.compBox.x - 200}px`;
      this.mouseYLast = newVal.mouseY;
    },
    deep: true,
  },
  mouseYLast(newVal) { // 鼠標移動Y座標
    this.forFlage = false;
    if (newVal - this.mouseYBefore > 30) {   // 移動30鼠標向下移,每移動30,moveBox移動一次
      this.comList.forEach((item, index) => {
        if (item === 'moveBox' && index < this.comLen - 1 && !this.forFlage) {
          this.nowCom = this.comList[index + 1];
          this.$set(this.comList, index + 1, 'moveBox');
          this.$set(this.comList, index, this.nowCom);
          this.mouseYBefore = newVal;
          this.forFlage = true;
        }
      });
    } else if (newVal - this.mouseYBefore < -30) {      // 鼠標向上移
      this.comList.forEach((item, index) => {
        if (item === 'moveBox' && index > 0 && !this.forFlage) {
          this.nowCom = this.comList[index - 1];
          // this.comList[index - 1] = 'moveBox';
          // this.comList[index] = this.nowCom;
         // this.comList[index]數組中採用這種方式賦值,vue是不能檢測到數組的變動的
          this.$set(this.comList, index - 1, 'moveBox');
          this.$set(this.comList, index, this.nowCom);
          this.mouseYBefore = newVal;
          this.forFlage = true;
        }
      });
    }
  },
},
其中forFlage的作用是,在forEach中不能使用break這樣來結束循環,所以用forFlage來表示,當遍歷到moveBox後, 就結束遍歷
5、methods
[JavaScript] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
methods: {
  getCom(val) {
    this.comRoute = [];
    val.forEach((item) => {
      this.comRoute.push(() => import(`./default/${item}`));
    });
  },
  getData(data, dataY, dataX) {  // 模塊組件點擊後,父組件中調用此方法,並傳值過來
    this.comType = data;
    this.transType = data;    // 目前考慮點擊後立即移動,點擊不移動的情況後期再考慮
    this.downFlag = true;
    this.mouseYBefore = dataY;
    this.$nextTick(() => {
      this.$refs.translateBox.style.top = `${dataY + this.scrollTop - 40 - this.compBox.y}px`;
      this.$refs.translateBox.style.left = `${dataX - this.compBox.x - 200}px`;
    });
  },
},
6、mounted
[JavaScript] 純文本查看 複製代碼
1
2
3
4
5
6
7
8
mounted() {
  this.comLen = this.comList.length;
  this.compBox = this.$refs.compBox.getBoundingClientRect();
  const that = this;
  window.onresize = () => (() => {
    that.compBox = this.$refs.compBox.getBoundingClientRect();
  })();
},
子組件
1、子組件template代碼
[JavaScript] 純文本查看 複製代碼
1
2
3
4
5
6
7
8
<div class="pad-box hover-box name-box">
  <p class="absolute-box">
    <i class="el-icon-rank operation-icon move-icon"       @mousedown="mouseDown"></i>
    <i class="el-icon-circle-plus operation-icon"></i>
    <i class="el-icon-s-tools operation-icon"></i>
  </p>
  教育經驗
</div>
2、script
[JavaScript] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
export default {
  name: 'educationExp',
  data() {
    return {
      comType: 'educationExp',
      mouseYBefore: 0,
      mouseXBefore: 0,
    };
  },
  methods: {
    mouseDown(e) {
      this.mouseYBefore = e.clientY;
      this.mouseXBefore = e.clientX;
      this.$emit('getData', this.comType, this.mouseYBefore, this.mouseXBefore);
    },
  },
};
文章轉載自

鏈接:https://juejin.im/post/5ec23dfa6fb9a04342682c7c
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章