实现动态定位弹窗

436 阅读1分钟

最近有收到一个需求,具体如下:

A组件订阅了消息,收到后端推送的消息时,要在B组件中找到对应元素并且弹窗显示详细信息

实现方案如下:

  1. 首先,A组件收到消息时,向B组件推送,通过postMessageaddEventListener两个api可实现,其他文章中有提到,不赘述;

  2. 然后,B组件收到消息时,找到对应的元素,并找到元素相对浏览器窗口的位置,确定弹窗的定位:

    这里要使用到 getBoundingClientRect 这个api,它的作用是获取元素相对于浏览器窗口的位置以及元素的宽高

  3. 需要注意:

     1. 要处理一下靠近页面边界的点位,如果不进行处理,弹窗会溢出,影响体验;
     2. 不同屏幕适配,最好将px转化为百分比;
    

代码如下:

<template>
	<div class="test">
		<ul>
			<li v-for="(item, index) in list" :key="index" :ref="'item-' + index" :style="{
				left: item.left + 'px',
				top: item.top + 'px'
			}" @click="clickItem(index)"></li>
		</ul>
		<div v-show="show" ref="modal" class="box" :style="{
			left: left + 'px',
			top: top + 'px'
		}"></div>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				show: false,
				left: 0,
				top: 0,
				list: [
					{
						left: 20,
						top: 20
					},
					{
						left: 1880,
						top: 20
					},
					{
						left: 20,
						top: 780
					},
					{
						left: 1880,
						top: 780
					},
					{
						left: 950,
						top: 400
					}
				]
			};
		},
		mounted() {
            // 用定时器模拟A组件的推送
			setTimeout(() => {
				this.clickItem(1);
			}, 3000);
		},
		methods: {
			clickItem(i) {
				const rect = this.$refs['item-' + i][0].getBoundingClientRect();
				const { x, y } = rect;
				const navH = 120;		// 导航栏高度
				this.left = x + 20;		// 相对元素偏移20px
				this.top = y + 20 - navH;

				// 处理边界周围的点
				const dowWidth = this.$refs.modal.getBoundingClientRect().width;
				const dowHeight = this.$refs.modal.getBoundingClientRect().height;
				const clientW = document.body.clientWidth;
				const clientH = document.body.clientHeight;
				if (dowWidth + this.left > clientW) {
					this.left = x - dowWidth;
				}
				if (dowHeight + this.top > clientH - navH) {
					this.top = y - navH - dowHeight;
				}
				this.show = true;
			}
		}
	};
</script>

<style lang="scss" scoped>
.test {
	position: relative;
	width: 100%;
	height: 100%;
	ul li {
		width: 20px;
		height: 20px;
		background-color: #0A1748;
		border-radius: 10px;
		position: absolute;
	}
	.box {
		width: 200px;
		height: 200px;
		border-radius: 10px;
		background-color: #0D357D;
		position: absolute;
	}
}
</style>

效果:

image-20210601161010308.png

image-20210601161021461.png