You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React日历组件实现点击元素位置缩放动效的需求咨询及代码优化

React日历组件实现点击元素位置缩放动效的需求咨询及代码优化

嘿,我完全懂你想要的那种交互效果——点击日历上的某个日期盒子,就从这个盒子的位置平滑缩放展开详情,而不是从中心缩放,这个动效确实能让日历体验提升不少!咱们一起来调整你的代码,实现类似参考案例里的丝滑动画。

核心问题分析

你当前的代码里,AnimatePresence管理的退出和进入动画都是基于容器中心的,没有利用点击元素的位置信息。要实现从点击位置缩放,我们需要:

  1. 捕获点击日期元素的位置、尺寸信息
  2. 把这些信息传递给动画组件,作为缩放的原点和初始状态依据

具体实现步骤及代码修改

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 11:29:05