一、问题
右键显示弹框,但是靠近浏览器边缘的部分会被隐藏,需要实现弹框位置自适应
二、 问题分析
如果想要最终弹框的宽高不超过屏幕视口,就等于屏幕视口的总宽/高减去弹框打开时的起点坐标,剩下的部分大于等于弹框的宽/高,简单来说,可以套用以下公式:
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><divclass="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部分的长度