如何用HTML/CSS/原生JS实现带动画的平滑页面跳转
平滑页面跳转动画实现方案
问题描述
我希望实现动画过程中完成页面跳转的效果,用户只看到动画运行,实际已跳转到新页面,达到平滑跳转而非生硬加载的效果。当前逻辑是点击按钮后,带图片的div展开动画,铺满全屏时执行跳转,期望类似metalab.com的跳转效果,但目前实现卡顿,尝试预加载资源无改善。最初尝试单文件管理所有内容,但复杂度高,转而用跳转方案。请问仅用HTML、CSS和原生JavaScript能否实现该平滑效果?我是前端初学者,不熟悉JS框架。
附当前代码:
<div class="buttonparent"> <button class="hover-btn" data-target="slide1">Ro</button> </div> <div id="slide1" class="slide-out-div left-to-right slide1-1"> <img src="./foto/codingpic.jpg" alt=""> </div> <style> body { background-color: rgb(0, 0, 0); height: 100vh; width: 100vw; overflow: hidden; margin: 0; padding: 0; } .buttonparent { display: flex; flex-direction: column; gap: 10px; position: absolute; top: 30%; left: 2%; } .hover-btn { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border: 1px solid transparent; border-radius: 50px; color: white; padding: 10px 20px; cursor: pointer; transition: border-color 0.3s ease, color 0.3s ease; text-align: center; font-size: 14px; box-sizing: border-box; font-family: "JetBrains Mono", monospace; font-weight: 100; } .hover-btn:hover { background: transparent; border-color: white; color: white; } .slide-out-div { position: absolute; width: 300px; height: 150px; opacity: 0; clip-path: inset(0 100% 0 0); transition: clip-path 0.5s ease-out, opacity 0.5s ease-out; } .slide-out-div.left-to-right { clip-path: inset(0 100% 0 0); opacity: 1; } .slide-out-div.left-to-right.active { clip-path: inset(0 0 0 0); opacity: 1; } .slide1-1 { top: 20vh; left: 20vw; width: 30vw; height: 15vw; } .slide-out-div.expanded { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; opacity: 1; z-index: 1; border-radius: 0; transition: all 0.8s ease-in; } body.expanded-mode { overflow: hidden; } img { width: 100%; height: 100%; } </style> <script> const buttons = document.querySelectorAll('.hover-btn'); const divs = document.querySelectorAll('.slide-out-div'); buttons.forEach((button) => { button.addEventListener('mouseenter', () => { divs.forEach((div) => div.classList.remove('active')); const targetId = button.getAttribute('data-target'); const targetDiv = document.getElementById(targetId); if (targetDiv) { targetDiv.classList.add('active'); } }); button.addEventListener('mouseleave', () => { const targetId = button.getAttribute('data-target'); const targetDiv = document.getElementById(targetId); if (targetDiv) { targetDiv.classList.remove('active'); } }); button.addEventListener('click', () => { const targetId = button.getAttribute('data-target'); const targetDiv = document.getElementById(targetId); if (targetDiv) { targetDiv.classList.add('expanded'); document.body.classList.add('expanded-mode'); document.querySelector('.buttonparent').style.display = 'none'; setTimeout(() => { window.location.href = 'diu.html'; }, 800); } }); }); function preloadResources() { fetch('diu.html') .then(response => response.text()) .then(data => { window.preloadedHTML = data; }) .catch(error => console.error('Error preloading diu.html:', error)); let link = document.createElement('link'); link.rel = 'preload'; link.href = 'diu.css'; link.as = 'style'; document.head.appendChild(link); } window.addEventListener('load', preloadResources); </script>
解决方案
完全可以用原生HTML/CSS/JS实现流畅的平滑跳转,当前卡顿的核心问题在于动画触发时机、性能优化和跳转逻辑的结合,以下是优化方案:
1. 优化动画性能
- 避免同时修改
position和clip-path,改用transform实现缩放/位移,利用GPU加速减少重排重绘 - 给动画元素添加
will-change: transform, opacity,让浏览器提前准备动画资源
2. 精准监听动画结束时机
放弃固定setTimeout,改用transitionend事件监听动画完成,确保跳转时机和动画完全同步
3. 优化预加载逻辑
预加载HTML到变量无法直接复用,改用<link rel="prefetch">预获取下一页资源,让浏览器提前缓存
修改后的完整代码
<div class="buttonparent"> <button class="hover-btn" data-target="slide1" data-href="diu.html">Ro</button> </div> <div id="slide1" class="slide-out-div left-to-right slide1-1"> <img src="./foto/codingpic.jpg" alt="Transition image"> </div> <style> body { background-color: #000; height: 100vh; width: 100vw; overflow: hidden; margin: 0; padding: 0; } .buttonparent { display: flex; flex-direction: column; gap: 10px; position: absolute; top: 30%; left: 2%; z-index: 10; } .hover-btn { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border: 1px solid transparent; border-radius: 50px; color: white; padding: 10px 20px; cursor: pointer; transition: border-color 0.3s ease, color 0.3s ease; text-align: center; font-size: 14px; box-sizing: border-box; font-family: "JetBrains Mono", monospace; font-weight: 100; } .hover-btn:hover { background: transparent; border-color: white; color: white; } .slide-out-div { position: fixed; top: 20vh; left: 20vw; width: 30vw; height: 15vw; opacity: 0; transform: scale(0.8); transition: transform 0.8s ease-in, opacity 0.8s ease-in; will-change: transform, opacity; z-index: 5; } .slide-out-div.left-to-right.active { opacity: 1; transform: scale(1); } .slide-out-div.expanded { top: 0; left: 0; width: 100vw; height: 100vh; opacity: 1; transform: scale(1); z-index: 9999; } body.expanded-mode { overflow: hidden; } img { width: 100%; height: 100%; object-fit: cover; } </style> <script> const buttons = document.querySelectorAll('.hover-btn'); const divs = document.querySelectorAll('.slide-out-div'); // 预加载下一页资源 function preloadResources() { buttons.forEach(btn => { const href = btn.getAttribute('data-href'); if (href) { // 预获取HTML const prefetchHtml = document.createElement('link'); prefetchHtml.rel = 'prefetch'; prefetchHtml.href = href; document.head.appendChild(prefetchHtml); // 预获取对应CSS(假设CSS文件名和HTML一致) const cssHref = href.replace('.html', '.css'); const prefetchCss = document.createElement('link'); prefetchCss.rel = 'prefetch'; prefetchCss.href = cssHref; prefetchCss.as = 'style'; document.head.appendChild(prefetchCss); } }); } buttons.forEach((button) => { // 鼠标 hover 显示预览 button.addEventListener('mouseenter', () => { divs.forEach((div) => div.classList.remove('active')); const targetId = button.getAttribute('data-target'); const targetDiv = document.getElementById(targetId); if (targetDiv) { targetDiv.classList.add('active'); } }); button.addEventListener('mouseleave', () => { const targetId = button.getAttribute('data-target'); const targetDiv = document.getElementById(targetId); if (targetDiv) { targetDiv.classList.remove('active'); } }); // 点击触发跳转动画 button.addEventListener('click', (e) => { e.preventDefault(); const targetId = button.getAttribute('data-target'); const targetDiv = document.getElementById(targetId); const targetHref = button.getAttribute('data-href'); if (!targetDiv || !targetHref) return; // 隐藏按钮容器 document.querySelector('.buttonparent').style.opacity = '0'; setTimeout(() => { document.querySelector('.buttonparent').style.display = 'none'; }, 300); // 触发展开动画 targetDiv.classList.remove('active'); targetDiv.classList.add('expanded'); // 监听动画结束后跳转 const handleTransitionEnd = () => { targetDiv.removeEventListener('transitionend', handleTransitionEnd); window.location.href = targetHref; }; targetDiv.addEventListener('transitionend', handleTransitionEnd); }); }); window.addEventListener('load', preloadResources); </script>
关键优化点说明
- 动画性能:用
transform替代clip-path和position切换,transform属于合成属性,由GPU处理,减少页面重排重绘 - 时机同步:通过
transitionend事件确保动画完全结束后再跳转,避免固定时间带来的不同设备上的差异 - 预加载优化:使用标准的
prefetch链接让浏览器主动缓存下一页资源,比手动fetch更高效,符合浏览器缓存机制 - 层级管理:明确设置元素
z-index,避免动画过程中出现层级错乱
内容的提问来源于stack exchange,提问作者JibGor
相关产品推荐
相关产品推荐

