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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值