如何为作品集网站实现非对称按钮的悬停缩放推挤动画?
问题分析与解决方案
你要实现的非对称按钮悬停放大+推挤交互,纯CSS可实现基础效果,但要完美复刻精细的动态逻辑,结合JavaScript会更灵活可控。先看你现有代码的核心问题:2x2等大Grid没有非对称布局,推挤逻辑仅覆盖hover元素后的兄弟节点,scale变换会导致元素重叠。
一、纯CSS优化方案(适合基础需求)
先调整Grid为非对称布局,针对每个位置的按钮单独编写hover规则,用transform-origin控制放大原点,避免偏移。
修改后的CSS
body, html { height: 100%; margin: 0; padding: 20px; box-sizing: border-box; } .container { display: grid; height: 100%; /* 非对称布局:第一列/行占比更大 */ grid-template-columns: 1.2fr 0.8fr; grid-template-rows: 1.2fr 0.8fr; gap: 15px; position: relative; } .button { display: flex; align-items: center; justify-content: center; background-color: #f0f0f0; border-radius: 8px; overflow: hidden; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); position: relative; z-index: 0; } .button button { width: 100%; height: 100%; border: none; background: transparent; font-size: 1.2rem; cursor: pointer; } /* 左上按钮:向右下放大,推挤右下两个按钮 */ .button:nth-child(1):hover { transform: scale(1.1); z-index: 1; transform-origin: top left; } .button:nth-child(1):hover ~ .button:nth-child(2) { transform: translateX(10%); } .button:nth-child(1):hover ~ .button:nth-child(3) { transform: translateY(10%); } .button:nth-child(1):hover ~ .button:nth-child(4) { transform: translate(10%, 10%); } /* 右上按钮:向左下放大,推挤左下两个按钮 */ .button:nth-child(2):hover { transform: scale(1.1); z-index: 1; transform-origin: top right; } .button:nth-child(2):hover ~ .button:nth-child(3) { transform: translate(-10%, 10%); } .button:nth-child(2):hover ~ .button:nth-child(4) { transform: translateY(10%); } /* 左下按钮:向右上放大,推挤右上两个按钮 */ .button:nth-child(3):hover { transform: scale(1.1); z-index: 1; transform-origin: bottom left; } .button:nth-child(3):hover ~ .button:nth-child(4) { transform: translateX(10%); } .button:nth-child(1):not(:hover) { transform: translateX(-10%); } .button:nth-child(2):not(:hover) { transform: translate(-10%, -10%); } /* 右下按钮:向左上放大,推挤左上两个按钮 */ .button:nth-child(4):hover { transform: scale(1.1); z-index: 1; transform-origin: bottom right; } .button:nth-child(1):not(:hover) { transform: translate(-10%, -10%); } .button:nth-child(2):not(:hover) { transform: translateY(-10%); } .button:nth-child(3):not(:hover) { transform: translateX(-10%); } .container:hover .button:not(:hover) { transition-delay: 0s; }
纯CSS局限:代码冗余,推挤距离固定,响应式适配繁琐。
二、JavaScript增强方案(完美复刻推荐)
用JS监听hover事件,动态计算每个按钮的推挤方向与距离,实现更自然的交互逻辑。
HTML(无需修改)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Portifólio João Pedro</title> <link rel="stylesheet" href="style.css"> </head> <body> <div class="container"> <div class="button"> <button>Botão 1</button> </div> <div class="button"> <button>Botão 2</button> </div> <div class="button"> <button>Botão 3</button> </div> <div class="button"> <button>Botão 4</button> </div> </div> <script src="script.js"></script> </body> </html>
CSS(简化基础样式)
body, html { height: 100%; margin: 0; padding: 20px; box-sizing: border-box; } .container { display: grid; height: 100%; grid-template-columns: 1.2fr 0.8fr; grid-template-rows: 1.2fr 0.8fr; gap: 15px; position: relative; } .button { display: flex; align-items: center; justify-content: center; background-color: #f0f0f0; border-radius: 8px; overflow: hidden; transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), z-index 0.3s; position: relative; z-index: 0; } .button button { width: 100%; height: 100%; border: none; background: transparent; font-size: 1.2rem; cursor: pointer; } .button.hovered { transform: scale(1.1); z-index: 1; }
JavaScript(核心交互逻辑)
const container = document.querySelector('.container'); const buttons = document.querySelectorAll('.button'); buttons.forEach(btn => { btn.addEventListener('mouseenter', (e) => { const hoverBtn = e.currentTarget; hoverBtn.classList.add('hovered'); const hoverRect = hoverBtn.getBoundingClientRect(); buttons.forEach(otherBtn => { if (otherBtn === hoverBtn) return; const otherRect = otherBtn.getBoundingClientRect(); // 计算相对中心位置的偏移 const dx = (otherRect.left + otherRect.width/2) - (hoverRect.left + hoverRect.width/2); const dy = (otherRect.top + otherRect.height/2) - (hoverRect.top + hoverRect.height/2); // 根据方向设置推挤距离 let tx = dx > 0 ? 10 : dx < 0 ? -10 : 0; let ty = dy > 0 ? 10 : dy < 0 ? -10 : 0; otherBtn.style.transform = `translate(${tx}%, ${ty}%)`; }); }); btn.addEventListener('mouseleave', () => { buttons.forEach(b => { b.classList.remove('hovered'); b.style.transform = ''; }); }); });
JS方案优势:动态计算推挤方向,代码可扩展性强,响应式适配更灵活,能完美匹配参考网站的交互细节。
内容的提问来源于stack exchange,提问作者NotprQn
相关产品推荐
相关产品推荐

