动画过渡前应读取并固化DOM布局尺寸(如getBoundingClientRect()返回值),而非使用浅拷贝;因offsetWidth等是只读实时值,须通过强制重排获取快照并直接保存数值。

在动画过渡期间暂存元素的初始布局尺寸,**不是浅拷贝的典型用途**,但“浅拷贝”这个动作常被误用于描述“保存一份当时的状态快照”。真正需要的是:在动画开始前,**读取并固化当前渲染后的尺寸值(如 width、height、top、left)**,后续用这些静态数值参与动画计算或回退逻辑。这和数组/对象的浅拷贝原理不同——它不涉及引用复制,而是对 DOM 布局数据的一次性采样。
为什么不能靠浅拷贝保存尺寸?
DOM 元素本身是对象,但它的 offsetWidth、getBoundingClientRect() 等属性是只读的实时计算值,不是可拷贝的数据字段。你无法通过 [...el] 或 Object.assign({}, el) 得到它的尺寸快照——这些操作要么报错,要么返回空/无效对象。
真正要保存的,是这些方法返回的数值结果,比如:
-
const rect = el.getBoundingClientRect()→ 返回一个 普通对象(含 left、top、width、height 等数字属性) -
const w = el.offsetWidth→ 返回一个 原始数字
这些结果本身是基本类型或普通对象,可直接赋值保存,无需“浅拷贝”介入。
立即学习“Java免费学习笔记(深入)”;
正确做法:在 transition 触发前读取并缓存尺寸
关键时机是:元素样式已应用、DOM 已渲染、但 transition 尚未开始(避免浏览器跳过重排)。常用技巧是强制触发重排(reflow),再读取:
- 先设置目标样式(如
el.style.transform = 'translateX(100px)') - 立即读取
el.offsetWidth或el.getBoundingClientRect()—— 这会强制同步计算当前布局 - 此时拿到的就是动画前的真实尺寸,可安全存入变量或 data 属性
示例:
const startRect = el.getBoundingClientRect();el.style.transition = 'transform 0.3s ease';
el.style.transform = 'translateX(200px)';
// ✅ 此时 startRect 已锁定初始位置,可用于动画中对比或 revert
如果非要“拷贝” getBoundingClientRect 返回的对象
它返回的是 DOMRect 实例(类数组对象),虽可浅拷贝,但仅当需多处复用或防意外修改时才必要:
-
const copy = { ...rect }—— 解构赋值,生成新普通对象(推荐) -
const copy = Object.assign({}, rect)—— 兼容老环境 -
const copy = JSON.parse(JSON.stringify(rect))—— 过重,不必要
注意:rect 中所有属性都是数字,不存在嵌套引用,所以任何一种“拷贝”都等价于深拷贝效果,无需纠结深浅。
结合动画过渡的典型场景
比如实现“缩放展开卡片”,需记录原始尺寸以便还原:
- 点击前:调用
getBoundingClientRect()并缓存为el.dataset.startRect = JSON.stringify(rect) - 动画中:用缓存值计算 scale 比例,或作为 CSS 自定义属性传入
style.setProperty('--start-width', rect.width + 'px') - 关闭时:用缓存值设回 transform/size,确保精准回弹


















