如何使用framer-motion实现子元素增减时div尺寸平滑过渡动画?
问题描述

当选择“全部”选项移除所有已选项目时,包裹这些选项的div会突然跳转收缩,我希望实现平滑的收缩动画,避免选项下方的分隔线越过“mindegy”选项。
以下是当前的部分代码(已选选项的渲染逻辑):
<AnimatePresence> {selectedOptions.map((option) => ( <motion.div layout initial={{ opacity: 0, scale: 0}} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0 }} key={option.value} className="flex items-center justify-center bg-slate-300 max-w-36 text-sm m-2 border border-slate-300 rounded-2xl"> <div className="p-1 ml-2">{option.text}</div> <X onClick={() => updateSelection(option, false)} className="ml-1 mr-1 cursor-pointer hover:text-gray-500 text-zinc-600" size={16} /> </motion.div> ))} </AnimatePresence>
解决方案
问题核心是父容器没有跟随子元素的退场动画做平滑尺寸过渡——当所有子元素同时执行exit动画时,AnimatePresence默认会在子元素动画结束后立即移除DOM,父容器直接跳转到空状态尺寸,导致突兀跳转。
你可以通过两步修复:
给父容器添加motion能力
将包裹<AnimatePresence>的普通div改成<motion.div>,并加上layout属性。这样父容器会自动感知子元素的尺寸变化,生成平滑的收缩动画。设置AnimatePresence的mode为"wait"
让AnimatePresence等待所有子元素的exit动画完成后再移除DOM,确保父容器有足够时间完成尺寸过渡。
修改后的完整代码示例:
{/* 把父容器改成motion.div,添加layout属性 */} <motion.div layout className="flex flex-wrap"> <AnimatePresence mode="wait"> {selectedOptions.map((option) => ( <motion.div layout initial={{ opacity: 0, scale: 0}} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0, transition: { duration: 0.3 } }} {/* 统一动画时长,和父容器节奏匹配 */} key={option.value} className="flex items-center justify-center bg-slate-300 max-w-36 text-sm m-2 border border-slate-300 rounded-2xl"> <div className="p-1 ml-2">{option.text}</div> <X onClick={() => updateSelection(option, false)} className="ml-1 mr-1 cursor-pointer hover:text-gray-500 text-zinc-600" size={16} /> </motion.div> ))} </AnimatePresence> </motion.div>
额外提示:
- 给子元素的
exit动画设置统一的transition.duration,能让子元素退场和父容器收缩的节奏更协调。 - 尽量保持父容器的布局样式简洁,避免复杂的padding/margin干扰layout动画的尺寸计算。
内容的提问来源于stack exchange,提问作者J.Doe
相关产品推荐
相关产品推荐

