vue模块移动组件

257 阅读3分钟
一直都想实现类似于五百丁中简历填写中模块跟随鼠标移动的组件,最近闲来无事,自己琢磨实现了一个差不多的组件。

其中每个模块都是组件调入(项目经验、教育经验、工作经验等),所以这里也用到了动态加载组件方式。
思路:鼠标移入模块,显示相应模块的点击移动按钮,点击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);
},
},
};
文章转载自

链接:juejin.cn/post/684490…