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

一、问题

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

二、 问题分析

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

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

相关推荐
_.Switch39 分钟前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光43 分钟前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   44 分钟前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   1 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web1 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常1 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇2 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr2 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho3 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常4 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js