vue自定义弹出式菜单PopupMenu(自适应屏幕点击位置,附源码及演示视频)
vue自定义弹出式菜单组件,其实就是在鼠标点击位置弹出一个绝对位置的div层,如果点击位置靠右或靠下,防止菜单超出屏幕,会向上或向左进行一定的位移,因为弹出菜单的使用场景会比较多,考虑封装成组件方便以后调用,减少重复开发。
·
实现原理
其实就是在鼠标点击位置弹出一个绝对位置的div层,如果点击位置靠右或靠下,防止菜单超出屏幕,会向上或向左进行一定的位移, 因为弹出菜单的使用场景会比较多,考虑封装成组件方便以后调用,减少重复开发。
效果如下
涉及到的样式类
1、固定定位,也叫绝对定位,因为要在光标的指定位置进行显示
.position-fixed {
position: fixed;
}
2、popup-menu菜单样式,z-index数字要大点,这样可以显示在最顶层,popup-menu-item为菜单项样式,popup-menu-item:hover为光标经过菜单项变色样式
.popup-menu {
width: 150px;
padding: 10px;
border-radius: 10px;
background-color:#fff;
border-style: solid;
border-width: 1px;
border-color: rgba(0, 0, 0, .2);
z-index: 999;
}
.popup-menu-item {
height: 30px;
}
.popup-menu-item:hover {
border-radius: 5px;
background: rgba(0, 0, 0, .1);
}
子组件代码
<template>
<div v-if="showMenu" class="position-fixed popup-menu flex flex-col" :style="{top:clientY, left:clientX}">
<div v-for="item in menuData"
class="popup-menu-item pointer flex flex-center-cz padding-left-m" :data-id="item.id"
@click="itemClick" >{{item.name}}</div>
</div>
</template>
<script>
export default {
name: 'PopupMenu',
props: {
menuData:{},
},
data() {
return {
showMenu: false,
clientX:'',
clientY:'',
}
},
methods: {
show(event) {
console.log("父组件传过来的event", event);
let x = event.x; //鼠标点击的x坐标
let y = event.y; //鼠标点击的y坐标
let menuWidth = 150;//菜单宽度,这个与.popup-menu样式对应的宽度一致
let menuHeight = this.menuData.length * 30 + 20; //菜单高度,根据菜单项自动计算的高度
this.clientX = (parseFloat(x) - 10) + "px";
this.clientY = (parseFloat(y) + 10) + "px";
let windowWidth = document.documentElement.clientWidth; // 实时屏幕宽度
let windowHeight = document.documentElement.clientHeight; // 实时屏幕高度
if (parseFloat(x) + menuWidth > windowWidth) {
this.clientX = (parseFloat(windowWidth) - menuWidth - 50) + "px";
}
if (parseFloat(y) + menuHeight > windowHeight) {
this.clientY = (parseFloat(windowHeight) -menuHeight - 50) + "px";
}
this.showMenu = true;
event.stopPropagation();//阻止事件冒泡
document.addEventListener("click", this.closeMenu, false);// 添加关闭事件
},
closeMenu() {
console.log("销毁监听事件。");
document.removeEventListener("click", this.closeMenu, false);//销毁关闭事件
this.showMenu = false;
},
itemClick(event) {
let id = event.target.getAttribute("data-id");//获取菜单项id
this.$emit('menuClick', id);//传参调用父组件事件,让父组件知道是点击到哪个菜单项
}
}
}
</script>
<style>
.popup-menu {
width: 150px;
padding: 10px;
border-radius: 10px;
background-color:#fff;
border-style: solid;
border-width: 1px;
border-color: rgba(0, 0, 0, .2);
z-index: 999;
}
.popup-menu-item {
height: 30px;
}
.popup-menu-item:hover {
border-radius: 5px;
background: rgba(0, 0, 0, .1);
}
</style>
父组件代码
<template>
<div class="body">
<div class="pnl-top" >
<div class="pointer cannotselect margin-top-l margin-left-l btn-blue-full" @click="popup">正常弹出</div>
<div class="pointer cannotselect margin-top-l margin-right-l btn-blue-full" @click="popup">向左弹出</div>
</div>
<div class="pnl-bottom" >
<div class="pointer cannotselect margin-top-l margin-left-l btn-blue-full" @click="popup">向上弹出</div>
<div class="pointer cannotselect margin-top-l margin-right-l btn-blue-full" @click="popup">左上弹出</div>
</div>
<popup-menu ref="child" :menuData="menuData" @menuClick="menuClick(arguments)" >
</popup-menu>
</div>
</template>
<script>
/*
名称:vue自定义弹出菜单
功能:
作者:唐赢
时间:2023-1-17
*/
import PopupMenu from '@/components/PopupMenu'
export default {
name: 'Main',
components: {
PopupMenu
},
data () {
return {
menuData:[{id:'rename',name:'重命名'},{id:'remove',name:'移动'},{id:'delete',name:'删除'}], //菜单数据
}
},
methods: {
popup(event) {
this.$refs.child.show(event);//调用子组件的show方法,用于显示弹出式菜单,引用组件时,需要 ref="child"
},
menuClick(args) {
// 子组件的方法 @menuClick="menuClick(arguments)" 注意menuClick()里面一定要填写arguments,用于接受子组件的传参
let id = args[0];
alert("您点击了:" + id);
}
},
}
</script>
<style scoped>
.body {
display: relative;
justify-content: center;
margin-top: 73px;
width: 100%;
}
.pnl-top {
position: absolute;
display: flex;
justify-content: space-between;
top: 70;
left: 0;
right: 0;
height: 60px;
}
.pnl-bottom {
position: absolute;
display: flex;
justify-content: space-between;
bottom: 0;
left: 0;
right: 0;
height: 60px;
}
</style>
演示及下载地址
更多推荐
已为社区贡献5条内容
所有评论(0)