如何在移动端/小屏幕避免created钩子创建的随机定位div重叠?
解决Vue中随机定位圆形元素在移动端/窗口缩小时的重叠问题
核心问题拆解
created钩子执行时机早于DOM挂载,无法获取容器和元素的实际渲染尺寸,初始位置计算基于默认值,适配移动端时尺寸变化后位置失效。- 窗口尺寸变化时未重新计算元素位置,导致原有位置在新尺寸空间下出现重叠。
解决方案步骤
- 替换钩子时机:用
mounted钩子替代created,此时DOM已完成挂载,可获取到容器和元素的真实尺寸。 - 监听窗口 resize 事件:在窗口尺寸变化时,重新计算所有元素的随机位置,避免重叠。
- 封装碰撞检测工具函数:基于当前容器和元素的实时尺寸,生成不重叠的随机坐标。
- 配合媒体查询:确保容器和元素的尺寸在不同屏幕下适配,为位置计算提供正确的基础尺寸。
完整组件代码示例
<template> <div class="circle-container" ref="container"> <div v-for="(circle, index) in circles" :key="index" class="circle" :style="{ left: `${circle.x}px`, top: `${circle.y}px` }" ></div> </div> </template> <script> export default { data() { return { circles: [], circleCount: 10, // 圆形元素数量 resizeTimer: null }; }, mounted() { // 初始化生成圆形位置 this.generateCircles(); // 监听窗口 resize 事件 window.addEventListener('resize', this.handleResize); }, beforeUnmount() { // 移除监听,避免内存泄漏 window.removeEventListener('resize', this.handleResize); if (this.resizeTimer) clearTimeout(this.resizeTimer); }, methods: { // 防抖处理 resize 事件,避免频繁触发 handleResize() { clearTimeout(this.resizeTimer); this.resizeTimer = setTimeout(() => { this.generateCircles(); }, 200); }, // 生成所有不重叠的圆形元素位置 generateCircles() { const container = this.$refs.container; if (!container) return; const containerWidth = container.offsetWidth; const containerHeight = container.offsetHeight; // 获取圆形元素的实时尺寸(由媒体查询控制) const circleSize = parseInt(getComputedStyle(document.querySelector('.circle')).width); const circles = []; for (let i = 0; i < this.circleCount; i++) { let newCircle; // 循环生成直到找到不重叠的位置 do { newCircle = this.getRandomCircle(containerWidth, containerHeight, circleSize); } while (this.isOverlap(newCircle, circles, circleSize)); circles.push(newCircle); } this.circles = circles; }, // 生成单个圆形的随机坐标 getRandomCircle(containerWidth, containerHeight, circleSize) { return { x: Math.floor(Math.random() * (containerWidth - circleSize)), y: Math.floor(Math.random() * (containerHeight - circleSize)) }; }, // 碰撞检测:判断新生成的圆形是否与已有圆形重叠 isOverlap(newCircle, existingCircles, circleSize) { const radius = circleSize / 2; const newCenterX = newCircle.x + radius; const newCenterY = newCircle.y + radius; return existingCircles.some(circle => { const existingCenterX = circle.x + radius; const existingCenterY = circle.y + radius; // 计算两个圆心的距离 const distance = Math.sqrt( Math.pow(newCenterX - existingCenterX, 2) + Math.pow(newCenterY - existingCenterY, 2) ); // 距离小于两个半径之和则重叠 return distance < circleSize; }); } } }; </script> <style scoped> .circle-container { width: 100%; height: 80vh; position: relative; overflow: hidden; border: 1px solid #eee; } .circle { position: absolute; border-radius: 50%; background-color: #42b983; } /* 媒体查询控制圆形尺寸 */ @media (min-width: 768px) { .circle { width: 60px; height: 60px; } } @media (max-width: 767px) { .circle { width: 40px; height: 40px; } } </style>
代码关键点说明
- mounted 钩子:确保DOM挂载后再获取容器尺寸,初始计算的位置基于真实渲染尺寸。
- 防抖的 resize 处理:避免窗口拖拽时频繁触发位置计算,提升性能。
- 碰撞检测逻辑:通过计算圆心距离判断是否重叠,确保所有元素在当前容器尺寸下无重叠。
- 媒体查询配合:不同屏幕下的圆形尺寸是位置计算的基础,确保坐标适配当前元素大小。
内容的提问来源于stack exchange,提问作者parsecer
相关产品推荐
相关产品推荐

