为Grid布局切换添加过渡动画的实现问题
问题描述
我创建了一个3*3的Grid布局,初始状态下div1占据4个网格单元格;当点击其他div时,该div会占据4个单元格,div1收缩为1个单元格。我尝试为div添加transition属性来减缓收缩与展开的速度,但动画并未生效。
原代码
App.jsx
import React, { useState } from 'react'; import './App.css'; const App = () => { const initialGrid = { div1: { gridArea: '1 / 1 / 3 / 3' }, div2: { gridArea: '1 / 3 / 2 / 4' }, div3: { gridArea: '2 / 3 / 3 / 4' }, div4: { gridArea: '3 / 3 / 4 / 4' }, div5: { gridArea: '3 / 2 / 4 / 3' }, div6: { gridArea: '3 / 1 / 4 / 2' }, }; const [grid, setGrid] = useState(initialGrid); const handleClick = (div) => { switch (div) { case 'div1': setGrid({ ...initialGrid, div1: { gridArea: '1 / 1 / 3 / 3' }, }); break; case 'div2': setGrid({ ...initialGrid, div1: { gridArea: '1 / 1 / 2 / 2' }, div2: { gridArea: '1 / 2 / 3 / 4' }, div3: { gridArea: '3 / 3 / 4/ 4' }, div4: { gridArea: '3 / 2 / 4 / 3' }, div5: { gridArea: '3 / 1 / 4 / 2' }, div6: { gridArea: '2 / 2 / 3/ 1' }, }); break; case 'div3': setGrid({ ...initialGrid, div1: { gridArea: '1 / 2 / 2 / 2' }, div2: { gridArea: '1 / 3 / 2 / 4' }, div3: { gridArea: '2 / 2 / 4/ 4' }, div4: { gridArea: '3 / 1 / 4 / 2' }, div5: { gridArea: '2 / 1 / 2 / 2' }, div6: { gridArea: '1 / 1 / 2 / 2' }, }); break; case 'div4': setGrid({ ...initialGrid, div1: { gridArea: '1 / 3 / 2 / 4' }, div2: { gridArea: '2 / 3 / 3 / 4' }, div3: { gridArea: '3 / 3 / 4 / 4' }, div4: { gridArea: '2 / 1 / 4 / 3' }, div5: { gridArea: '1 / 1 / 2 / 2' }, div6: { gridArea: '1 / 2 / 2 / 2' }, }); break; case 'div5': setGrid({ ...initialGrid, div1: { gridArea: '2 / 3 / 3 / 4' }, div2: { gridArea: '1 / 3 / 2 / 4' }, div3: { gridArea: '3 / 2 / 4 / 3' }, div4: { gridArea: '3 / 3 / 4 / 4' }, div5: { gridArea: '1 / 1 / 3 / 3' }, div6: { gridArea: '3 / 1 / 4 / 2' }, }); break; case 'div6': setGrid({ ...initialGrid, div1: { gridArea: '1 / 1 / 2 / 2' }, div2: { gridArea: '1 / 3 / 2 / 4' }, div3: { gridArea: '2 / 3 / 3 / 4' }, div4: { gridArea: '3 / 3 / 4 / 4' }, div5: { gridArea: '1 / 2 / 2 / 2' }, div6: { gridArea: '2 / 1 / 4 / 3' }, }); break; default: setGrid(initialGrid); } }; return ( <div className="grid-container"> <div className="grid-item" style={{ ...grid.div1, backgroundColor: 'red' }} onClick={() => handleClick('div1')} > 1 </div> <div className="grid-item" style={{ ...grid.div2, backgroundColor: 'blue' }} onClick={() => handleClick('div2')} > 2 </div> <div className="grid-item" style={{ ...grid.div3, backgroundColor: 'green' }} onClick={() => handleClick('div3')} > 3 </div> <div className="grid-item" style={{ ...grid.div4, backgroundColor: 'black' }} onClick={() => handleClick('div4')} > 4 </div> <div className="grid-item" style={{ ...grid.div5, backgroundColor: 'orange' }} onClick={() => handleClick('div5')} > 5 </div> <div className="grid-item" style={{ ...grid.div6, backgroundColor: 'purple' }} onClick={() => handleClick('div6')} > 6 </div> </div> ); }; export default App;
App.css
.grid-container { display: grid; grid-template-rows: repeat(4, 100px); grid-template-columns: repeat(4, 100px); gap: 5px; width: 410px; margin: 0 auto; transition: all 2000ms ease-in-out; } .grid-item { display: flex; align-items: center; justify-content: center; border: 1px solid #000; font-size: 2.5rem; color: white; transition: all 2000ms ease-in-out; }
解决方案
问题原因
CSS的transition属性仅支持可插值的数值型属性,而grid-area是复合属性,其值为四个离散位置值的组合(行起始/列起始/行结束/列结束),浏览器无法对这类离散值进行平滑过渡计算,因此添加transition后无动画效果。
修复步骤
- 将复合属性
gridArea拆分为单独的gridRowStart、gridRowEnd、gridColumnStart、gridColumnEnd属性,这些均为数值型属性,支持过渡动画。 - 调整React状态中的网格布局定义,使用拆分后的行列属性替代
gridArea。 - 在CSS中明确指定过渡的网格属性(或保留
all),确保浏览器能识别可过渡的属性。
修改后的代码
App.jsx
import React, { useState } from 'react'; import './App.css'; const App = () => { // 使用拆分的行列属性替代复合gridArea const initialGrid = { div1: { gridRowStart: 1, gridRowEnd: 3, gridColumnStart: 1, gridColumnEnd: 3 }, div2: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 3, gridColumnEnd: 4 }, div3: { gridRowStart: 2, gridRowEnd: 3, gridColumnStart: 3, gridColumnEnd: 4 }, div4: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 3, gridColumnEnd: 4 }, div5: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 2, gridColumnEnd: 3 }, div6: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 1, gridColumnEnd: 2 }, }; const [grid, setGrid] = useState(initialGrid); const handleClick = (div) => { switch (div) { case 'div1': setGrid(initialGrid); break; case 'div2': setGrid({ ...initialGrid, div1: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 1, gridColumnEnd: 2 }, div2: { gridRowStart: 1, gridRowEnd: 3, gridColumnStart: 2, gridColumnEnd: 4 }, div3: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 3, gridColumnEnd: 4 }, div4: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 2, gridColumnEnd: 3 }, div5: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 1, gridColumnEnd: 2 }, div6: { gridRowStart: 2, gridRowEnd: 3, gridColumnStart: 1, gridColumnEnd: 2 }, }); break; case 'div3': setGrid({ ...initialGrid, div1: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 2, gridColumnEnd: 3 }, div2: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 3, gridColumnEnd: 4 }, div3: { gridRowStart: 2, gridRowEnd: 4, gridColumnStart: 2, gridColumnEnd: 4 }, div4: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 1, gridColumnEnd: 2 }, div5: { gridRowStart: 2, gridRowEnd: 3, gridColumnStart: 1, gridColumnEnd: 2 }, div6: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 1, gridColumnEnd: 2 }, }); break; case 'div4': setGrid({ ...initialGrid, div1: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 3, gridColumnEnd: 4 }, div2: { gridRowStart: 2, gridRowEnd: 3, gridColumnStart: 3, gridColumnEnd: 4 }, div3: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 3, gridColumnEnd: 4 }, div4: { gridRowStart: 2, gridRowEnd: 4, gridColumnStart: 1, gridColumnEnd: 3 }, div5: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 1, gridColumnEnd: 2 }, div6: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 2, gridColumnEnd: 3 }, }); break; case 'div5': setGrid({ ...initialGrid, div1: { gridRowStart: 2, gridRowEnd: 3, gridColumnStart: 3, gridColumnEnd: 4 }, div2: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 3, gridColumnEnd: 4 }, div3: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 2, gridColumnEnd: 3 }, div4: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 3, gridColumnEnd: 4 }, div5: { gridRowStart: 1, gridRowEnd: 3, gridColumnStart: 1, gridColumnEnd: 3 }, div6: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 1, gridColumnEnd: 2 }, }); break; case 'div6': setGrid({ ...initialGrid, div1: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 1, gridColumnEnd: 2 }, div2: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 3, gridColumnEnd: 4 }, div3: { gridRowStart: 2, gridRowEnd: 3, gridColumnStart: 3, gridColumnEnd: 4 }, div4: { gridRowStart: 3, gridRowEnd: 4, gridColumnStart: 3, gridColumnEnd: 4 }, div5: { gridRowStart: 1, gridRowEnd: 2, gridColumnStart: 2, gridColumnEnd: 3 }, div6: { gridRowStart: 2, gridRowEnd: 4, gridColumnStart: 1, gridColumnEnd: 3 }, }); break; default: setGrid(initialGrid); } }; return ( <div className="grid-container"> <div className="grid-item" style={{ ...grid.div1, backgroundColor: 'red' }} onClick={() => handleClick('div1')} > 1 </div> <div className="grid-item" style={{ ...grid.div2, backgroundColor: 'blue' }} onClick={() => handleClick('div2')} > 2 </div> <div className="grid-item" style={{ ...grid.div3, backgroundColor: 'green' }} onClick={() => handleClick('div3')} > 3 </div> <div className="grid-item" style={{ ...grid.div4, backgroundColor: 'black' }} onClick={() => handleClick('div4')} > 4 </div> <div className="grid-item" style={{ ...grid.div5, backgroundColor: 'orange' }} onClick={() => handleClick('div5')} > 5 </div> <div className="grid-item" style={{ ...grid.div6, backgroundColor: 'purple' }} onClick={() => handleClick('div6')} > 6 </div> </div> ); }; export default App;
App.css
.grid-container { display: grid; grid-template-rows: repeat(4, 100px); grid-template-columns: repeat(4, 100px); gap: 5px; width: 410px; margin: 0 auto; } .grid-item { display: flex; align-items: center; justify-content: center; border: 1px solid #000; font-size: 2.5rem; color: white; /* 明确指定过渡的网格属性,比all更高效 */ transition: grid-row-start 0.5s ease, grid-row-end 0.5s ease, grid-column-start 0.5s ease, grid-column-end 0.5s ease; }
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

