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

一、问题

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

二、 问题分析

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

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

相关推荐
excel6 分钟前
webpack 核心编译器 十四 节
前端
excel13 分钟前
webpack 核心编译器 十三 节
前端
腾讯TNTWeb前端团队7 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰11 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪11 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪11 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy11 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom12 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom12 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom12 小时前
React与Next.js:基础知识及应用场景
前端·面试·github