如何自定义右键弹框并实现位置自适应?

一、问题

右键显示弹框,但是靠近浏览器边缘的部分会被隐藏,需要实现弹框位置自适应

二、 问题分析

如果想要最终弹框的宽高不超过屏幕视口,就等于屏幕视口的总宽/高减去弹框打开时的起点坐标,剩下的部分大于等于弹框的宽/高,简单来说,可以套用以下公式:

1.1 屏幕视口宽(clientWidth) - 鼠标点击的x轴(pageX) >= 弹框宽

1.2 屏幕视口高(clientHeight) - 鼠标点击y轴(pageY) >= 弹框高

三、实现步骤(vue3+ts)

1.首先获取屏幕视口的宽高

TypeScript 复制代码
	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.自定义的弹框组件

TypeScript 复制代码
<!--
 * @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.组件展示

TypeScript 复制代码
<!--
 * @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部分的长度

相关推荐
恋猫de小郭29 分钟前
Flutter Widget IDE 预览新进展,开始推进落地发布
android·前端·flutter
jingling5551 小时前
【Vue3 实战】插槽封装与懒加载
前端·javascript·vue.js
武昌库里写JAVA3 小时前
39.剖析无处不在的数据结构
java·vue.js·spring boot·课程设计·宠物管理
Freedom风间7 小时前
前端优秀编码技巧
前端·javascript·代码规范
萌萌哒草头将军7 小时前
🚀🚀🚀 Openapi:全栈开发神器,0代码写后端!
前端·javascript·next.js
萌萌哒草头将军7 小时前
🚀🚀🚀 Prisma 爱之初体验:一款非常棒的 ORM 工具库
前端·javascript·orm
拉不动的猪7 小时前
SDK与API简单对比
前端·javascript·面试
runnerdancer8 小时前
微信小程序蓝牙通信开发之分包传输通信协议开发
前端
BillKu8 小时前
Vue3后代组件多祖先通讯设计方案
开发语言·javascript·ecmascript
山海上的风8 小时前
Vue里面elementUi-aside 和el-main不垂直排列
前端·vue.js·elementui