React日历组件实现点击元素位置缩放动效的需求咨询及代码优化
React日历组件实现点击元素位置缩放动效的需求咨询及代码优化
嘿,我完全懂你想要的那种交互效果——点击日历上的某个日期盒子,就从这个盒子的位置平滑缩放展开详情,而不是从中心缩放,这个动效确实能让日历体验提升不少!咱们一起来调整你的代码,实现类似参考案例里的丝滑动画。
核心问题分析
你当前的代码里,AnimatePresence管理的退出和进入动画都是基于容器中心的,没有利用点击元素的位置信息。要实现从点击位置缩放,我们需要:
- 捕获点击日期元素的位置、尺寸信息
- 把这些信息传递给动画组件,作为缩放的原点和初始状态依据
具体实现步骤及代码修改
1. 添加状态存储点击元素的位置信息
首先,在Calendar组件里新增一个状态,用来保存点击日期元素的DOM矩形信息:
const [selectedDayRect, setSelectedDayRect] = useState(null);
2. 修改点击事件,捕获元素位置
更新handleDayClick函数,在设置选中日期的同时,获取点击元素相对于父容器的位置(因为父容器是relative定位,这样计算的坐标更准确):
const handleDayClick = (day, e) => { const rect = e.currentTarget.getBoundingClientRect(); const parentRect = e.currentTarget.offsetParent.getBoundingClientRect(); // 计算相对于父容器的坐标和尺寸 const relativeRect = { x: rect.left - parentRect.left, y: rect.top - parentRect.top, width: rect.width, height: rect.height }; setSelectedDayRect(relativeRect); setSelectedDay(day); };
同时,在渲染日期元素时,把事件对象传递给点击函数:
<div onClick={(e) => handleDayClick(el, e)} className="h-16 border p-2 hover:bg-gray-100 transform" key={idx} > {format(el, "dd")} </div>
3. 调整退出动画的缩放原点
修改日历网格的motion.div退出动画,让它从点击元素的位置开始缩放:
<motion.div exit={{ scale: 6, // 设置缩放原点为点击元素的中心位置 transformOrigin: selectedDayRect ? `${selectedDayRect.x + selectedDayRect.width/2}px ${selectedDayRect.y + selectedDayRect.height/2}px` : "center" }} transition={{ duration: 0.8, ease: "easeInOut", }} className="grid grid-cols-7" >
4. 调整详情组件的进入动画
让详情组件从点击元素的位置和尺寸开始,缩放展开到目标状态:
{selectedDay !== null && ( <motion.div initial={{ scale: 0, top: selectedDayRect.y, left: selectedDayRect.x, width: selectedDayRect.width, height: selectedDayRect.height, transformOrigin: "center" }} animate={{ scale: 1, top: 0, left: 0, width: "100%", height: "auto" }} transition={{ duration: 0.8, ease: "easeInOut", }} className="absolute top-0 left-0 bg-white p-4 border rounded" > <div> <h2>{format(selectedDay, "dd-MM-yyyy")}</h2> <h1>Going onward</h1> <p> Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quae placeat, rem, doloremque, tempore optio repellat architecto earum esse repellendus nemo tempora qui accusantium sapiente ullam inventore id quia explicabo dignissimos? </p> <button className="mt-4 p-2 border" onClick={() => setSelectedDay(null)} > Back to Calendar </button> </div> </motion.div> )}
这里注意给详情组件加上背景色,避免动画过程中透出下面的日历,同时把日期改成动态的selectedDay,而不是固定的20-2-2023。
额外优化思路
- 返回动画优化:现在点击Clear按钮是直接隐藏详情,你可以给详情组件添加退出动画,让它缩回到原来的日期位置,再切换回日历视图
- 层级管理:给详情组件添加更高的
z-index,确保动画过程中不会被其他元素遮挡 - 响应式适配:如果是移动端,要注意计算位置时考虑滚动条的偏移,可以用
window.scrollY来修正坐标 - 动画细节打磨:可以给缩放动画加上一点点延迟,或者调整缓动函数,让动效更贴近参考案例的质感
备注:内容来源于stack exchange,提问作者user16073989
相关产品推荐
相关产品推荐

