如何实现两种CSS Grid布局间的平滑过渡效果?
如何实现Grid布局中元素跨列/行的平滑过渡
我能通过切换元素类实现两种Grid布局:普通类占1列1行,聚焦类占2列2行,但添加transition: all 1s完全无效——背景色这类属性能过渡,但Grid结构切换是瞬间完成的。想让元素在1秒内滑动到位,不是跳变。
原代码
JS
/* add/remove class to an element */ function changeClass(fromid, toclass) { var itemdb = document.getElementById(fromid); var object_class = itemdb.getAttribute("class"); document.querySelectorAll('.item_focus').forEach(it => { it.classList.remove("item_focus"); it.removeAttribute("style"); it.classList.add("item"); it.style.backgroundColor = "lime"; }) if(object_class === "item_focus") { itemdb.classList.add("item"); }else { itemdb.removeAttribute("class"); itemdb.classList.add(toclass); if(Number.isInteger(fromid/3)) { itemdb.style.gridColumn = "2 / span 2"; itemdb.style.gridRow = fromid/3 + " / span 2"; } } }
CSS
.shop-container { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-gap: 25px; padding: 25px 10% 25px 10%; transition: .25s; } .item { background-color: red; grid-column: span 1; grid-row: span 1; width: 100%; position:relative; z-index: 5; overflow: hidden; cursor: pointer; } .item_focus { background-color: red; grid-column: span 2; grid-row: span 2; width: 100%; position:relative; z-index: 5; overflow: hidden; cursor: pointer; }
HTML
<div class='shop-container'> <div class='item' id='1' onclick='changeClass("1", "item_focus")'>box 1</div> <div class='item' id='2' onclick='changeClass("2", "item_focus")'>box 2</div> <div class='item' id='3' onclick='changeClass("3", "item_focus")'>box 3</div> <div class='item' id='4' onclick='changeClass("4", "item_focus")'>box 4</div> <div class='item' id='5' onclick='changeClass("5", "item_focus")'>box 5</div> <div class='item' id='6' onclick='changeClass("6", "item_focus")'>box 6</div> </div>
解决方案
核心逻辑
grid-column/grid-row属于离散的轨道定位属性,浏览器无法计算中间过渡状态,所以直接加transition没用。我们可以通过让容器自动填充空隙+给元素尺寸/层级加过渡来模拟平滑效果。
修改后的实现代码
HTML(无需改动)
<div class='shop-container'> <div class='item' id='1' onclick='changeClass("1", "item_focus")'>box 1</div> <div class='item' id='2' onclick='changeClass("2", "item_focus")'>box 2</div> <div class='item' id='3' onclick='changeClass("3", "item_focus")'>box 3</div> <div class='item' id='4' onclick='changeClass("4", "item_focus")'>box 4</div> <div class='item' id='5' onclick='changeClass("5", "item_focus")'>box 5</div> <div class='item' id='6' onclick='changeClass("6", "item_focus")'>box 6</div> </div>
CSS(关键修改)
.shop-container { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-gap: 25px; padding: 25px 10% 25px 10%; transition: all 0.5s ease; grid-auto-flow: dense; /* 让后面元素自动填充空隙,避免布局跳变 */ } .item { background-color: red; grid-column: span 1; grid-row: span 1; display: flex; align-items: center; justify-content: center; /* 给所有可过渡的属性加动画 */ transition: all 1s cubic-bezier(0.4, 0, 0.2, 1); position: relative; z-index: 1; overflow: hidden; cursor: pointer; } .item_focus { grid-column: span 2; grid-row: span 2; z-index: 10; /* 聚焦元素置顶,避免被其他元素遮挡 */ transition: all 1s cubic-bezier(0.4, 0, 0.2, 1); }
JS(简化逻辑)
function changeClass(fromid, toclass) { const targetItem = document.getElementById(fromid); const isFocused = targetItem.classList.contains('item_focus'); // 重置所有已聚焦的元素 document.querySelectorAll('.item_focus').forEach(item => { item.classList.remove('item_focus'); item.classList.add('item'); }); if (!isFocused) { targetItem.classList.add(toclass); // 保留第3、6个元素的特殊位置处理 if (Number.isInteger(parseInt(fromid)/3)) { targetItem.style.gridColumn = "2 / span 2"; targetItem.style.gridRow = `${parseInt(fromid)/3} / span 2`; } } }
效果说明
- 利用
grid-auto-flow: dense让布局重排时自动填充空隙,避免元素突然跳动 - 给
.item的尺寸、层级等可计算属性加过渡,浏览器能生成中间动画帧,实现滑动效果 - 用
cubic-bezier曲线调整过渡节奏,比线性过渡更自然
内容的提问来源于stack exchange,提问作者Fred Fredrik
相关产品推荐
相关产品推荐

