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

一、问题

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

二、 问题分析

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

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部分的长度

相关推荐
是萝卜干呀3 分钟前
Frontend - 防止多次请求,避免重复请求
javascript·ajax·jquery·防抖·节流·disabled属性
三金121389 分钟前
局部使用Vue
前端·javascript·vue.js
applebomb14 分钟前
【vue3+Typescript】unapp+stompsj模式下替代plus-websocket的封装模块
websocket·typescript·vue·uniapp·plus-websocket
LinXunFeng15 分钟前
Flutter - 子部件任意位置观察滚动数据
前端·flutter·开源
许静知15 分钟前
第十章 JavaScript的应用
开发语言·javascript·ecmascript
好开心3330 分钟前
js高级06-ajax封装和跨域
开发语言·前端·javascript·ajax·okhttp·ecmascript·交互
小镇程序员35 分钟前
vue2 src_Todolist消息订阅版本
前端·javascript·vue.js
Zack No Bug43 分钟前
解决报错:rror: error:0308010C:digital envelope routines::unsupported
前端·javascript·vue.js
飞奔的波大爷1 小时前
springboot vue工资管理系统源码和答辩PPT论文
vue.js·spring boot·后端
QTX187301 小时前
原生JS和CSS,HTML实现开屏弹窗
javascript·css·html