You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 23:24:54