一、问题
右键显示弹框,但是靠近浏览器边缘的部分会被隐藏,需要实现弹框位置自适应
二、 问题分析
如果想要最终弹框的宽高不超过屏幕视口,就等于屏幕视口的总宽/高减去弹框打开时的起点坐标,剩下的部分大于等于弹框的宽/高,简单来说,可以套用以下公式:
1.1 屏幕视口宽(clientWidth) - 鼠标点击的x轴(pageX) >= 弹框宽
1.2 屏幕视口高(clientHeight) - 鼠标点击y轴(pageY) >= 弹框高
三、实现步骤(vue3+ts)
1.首先获取屏幕视口的宽高
const windowWidth = ref(document.documentElement.clientWidth);
const windowHeight = ref(document.documentElement.clientHeight);
2.获取弹框打开时的起点,也就是当前鼠标点击的位置
宽 :e.pageX
高:e.pageY
3.获取弹框的宽高
在此我给的是固定宽500px高300px,如果是动态宽高请自行获取
4.给弹框设置固定定位,对应的left和top值如下:
left: windowWidth - e.pageX >= 500 ? e.pageX : windowWidth - 500,
top: windowHeight - e.pageY >= 300 ? e.pageY : windowHeight - 300,
四、完整代码
1.自定义的弹框组件
<!--
* @Description: 自定义右键弹框组件
* @FilePath: \Vue3-demo\src\myComponents\rightClickPopUpBox\index.vue
-->
<template>
<div
class="container"
:style="{
left: state.positionStyle.left + 'px',
top: state.positionStyle.top + 'px',
}"
v-if="state.visible"
>
<div class="content">{{ state.title }}</div>
<div class="footer">
<el-button size="default" type="success">保存</el-button>
<el-button size="default" type="primary" @click="closeDialog">取消</el-button>
</div>
</div>
</template>
<script setup lang="ts" name="popUpBox">
import { reactive, ref } from 'vue';
const state: any = reactive({
visible: false, // 是否显示
title: '',
positionStyle: {},
});
const openDialog = (data: any) => {
state.title = '按钮' + data.item + '的内容区域';
state.visible = true;
// 获取屏幕视口大小
const windowWidth = ref(document.documentElement.clientWidth);
const windowHeight = ref(document.documentElement.clientHeight);
state.positionStyle = {
// 这种方案不好,因为鼠标点击位置可能超出屏幕视口
// top: data.e.pageY,
// left: data.e.pageX,
left: windowWidth.value - data.e.pageX >= 500 ? data.e.pageX : windowWidth.value - 500,
top: windowHeight.value - data.e.pageY >= 300 ? data.e.pageY : windowHeight.value - 300,
};
};
const closeDialog = () => {
state.visible = false;
};
defineExpose({
openDialog,
closeDialog,
});
</script>
<style lang="scss" scoped>
.container {
position: fixed;
width: 500px;
height: 300px;
border: 1px solid #000;
padding: 15px;
background-color: #bbc1ff;
.content {
width: 100%;
height: 80%;
display: flex;
justify-content: center;
align-items: center;
background-color: #fff;
font-weight: bold;
font-size: 20px;
}
.footer {
flex: 1;
display: flex;
justify-content: center;
margin-top: 20px;
}
}
</style>
2.组件展示
<!--
* @Description: 我的组件-自定义右键弹框
* @FilePath: \Vue3-demo\src\views\showMyComponents\rightClickPopUpBox\index.vue
-->
<template>
<div class="layout-container layout-padding" @click="cancelPop($event)">
<div class="boxList">
<el-button size="large" type="primary" v-for="item in 10" :key="item" @contextmenu.prevent="handleContextMenu($event, item)">{{
'按钮' + item + ' : 请点击右键'
}}</el-button>
</div>
<PopUpBox ref="popUpBoxRef" class="popUpBox" />
</div>
</template>
<script setup lang="ts" name="rightClickPopUpBox">
import { ref } from 'vue';
import PopUpBox from '/@/myComponents/rightClickPopUpBox/index.vue';
const popUpBoxRef = ref();
// 右键事件-打开弹框
const handleContextMenu = (e: MouseEvent, item: any) => {
console.log(e, '右键坐标');
const data = { e, item };
popUpBoxRef.value.openDialog(data);
};
// 点击弹框以外的地方关闭弹框
const cancelPop = (event: any) => {
const box = document.querySelector('.popUpBox');
if (box) {
if (!box.contains(event.target)) {
popUpBoxRef.value.closeDialog();
}
}
};
</script>
<style lang="scss" scoped>
.boxList {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100px;
height: 50px;
}
</style>
五、补充
1.clientX、clientY
点击位置距离当前body可视区域的x,y坐标
2.pageX、pageY
对于整个页面来说,包括了被卷去的body部分的长度
3.screenX、screenY
对于整个屏幕来说(点击位置距离当前电脑屏幕的x,y坐标),包括了被卷去的body部分的长度
4.offsetX、offsetY
对于当前元素来说(相对于带有定位的父盒子的x,y坐标),包括了被卷去的body部分的长度
5.x、y
对于当前元素来说,不包括被卷去的body部分的长度