如何实现弹窗始终精准定位在目标日历事件元素正上方
日历弹窗定位问题求助
我正在开发一个日历项目,需求是点击选中的事件后,弹窗要精准显示在被点击元素的正上方。最开始我尝试基于点击元素的物理位置来实现,但效果非常不稳定,弹窗总是出现偏移(附件图片能展示这个问题,忽略弹窗内文本的异常格式,隐私内容已做删减)。
我想请教有没有可靠的方法,能让弹窗始终相对于黄色事件元素(就是日历中数字13上方的黄色事件框,不是底部的那个)固定在正上方,而且不管页面尺寸怎么变化,弹窗都能牢牢锁定这个位置?
以下是我尝试实现该功能的代码:
const Cal = () => { const [popupBoxVisibility, usePopupBoxVisibility] = useState('hidden'); //控制弹窗的显示/隐藏状态 const [clientPosition, useClientPosition] = useState([0, 0]); //用于计算弹窗的定位 const [eventDetails, useEventDetails] = useState(null); //弹窗中要展示的事件详情数据 const [currMonth, useCurrMonth] = useState(null); //当前月份,用来动态调整前后切换箭头的间距 const [currDate, useCurrDate] = useState(null); //当前点击事件的日期 const calendarRef = useRef(null); //FullCalendar组件的ref,用于判断组件是否挂载完成 const containerRef = useRef(null); //用于避免页面滚动影响弹窗定位 const handleEventClick = (e) => { //点击事件的处理逻辑:原本想通过日历总宽度430px、7天均分来计算事件中点,但因为弹窗需要额外margin调整,最终效果不理想 useEventDetails(e.event); //把当前点击的事件详情存入状态,传给Popupbox组件 const pastClientPosition = clientPosition; //更新前的弹窗位置 const pastEventDate = currDate; //上一次点击事件的日期 const eventRect = e.el.getBoundingClientRect(); const containerRect = containerRef.current.getBoundingClientRect(); const newClientPosition = [ eventRect.left - containerRect.left, eventRect.top - containerRect.top, ]; //获取当前点击事件的边界盒信息 useClientPosition(newClientPosition); //更新弹窗位置状态 useCurrDate(e.event.startStr); console.log(`currDate: ${currDate}`); console.log(`pastEventDate: ${pastEventDate}`); if ( JSON.stringify(pastEventDate) === JSON.stringify(e.event.startStr) || JSON.stringify(pastClientPosition) === '[0,0]' ) { //判断是否点击了同一个事件,或者是首次点击,切换弹窗的显示/隐藏状态 usePopupBoxVisibility( popupBoxVisibility === 'visible' ? 'hidden' : 'visible' ); } else if ( JSON.stringify(pastEventDate) !== JSON.stringify(e.event.startStr) ) { //处理特殊场景:如果之前点击事件后隐藏了弹窗,再点击不同事件时直接显示弹窗,避免无响应的情况 usePopupBoxVisibility('visible'); } }; const handleEventEnter = (e) => { //鼠标悬浮事件时,修改事件背景色 e.el.style.backgroundColor = '#ffce78'; }; const handleEventLeave = (e) => { //鼠标离开事件时,恢复原背景色 e.el.style.backgroundColor = '#DCAA47'; }; return ( <section className="calendar-section" ref={containerRef}> {/* 注:原代码中前后切换箭头的JSX部分存在内容截断,此处保留原样 */} {/* <div className="prev-button" onClick={(e) => { if (calendarRef.current) { const calendar = calendarRef.current.getApi(); calendar.prev(); } }} style={{ marginLeft: calendarRef.current ? {isBigScreen} ? `${-110 - 3 * currMonth.length}px` : `${80 - 3* currMonth.length}px` : `-110px`, marginTop: {isBigScreen} ? '133.5px' : '14px', }} ></div> <div className="next-button" onClick={(e) => { if (calendarRef.current) { const calendar = calendarRef.current.getApi(); calendar.next(); } }} style={{ marginLeft: calendarRef.current ? {isBigScreen} ? `${25 + 3 * currMonth.length}px` : `${-90 + 3 * currMonth.length}` : `25px` }} ></div> */} {/* 此处应包含FullCalendar组件及Popupbox组件的代码,原内容未完整提供 */} </section> ); };
内容的提问来源于stack exchange,提问作者zichyboy
相关产品推荐
相关产品推荐

