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

BarbaJS动画异常:返回首页时动画卡顿停止问题排查

问题解决:BarbaJS页面切换动画黑屏冻结问题

需求说明

  • 每次进入首页时,对应的入场动画需正常触发
  • 不同页面间切换需使用不同类型的动画

问题现象

从项目页返回首页时,动画停止,出现黑屏冻结,无法正常完成过渡。

复现步骤

  • 滚动至“Kiddy”项目并点击VIEW PROJECT
  • 待动画完成后,滚动至页面顶部点击LOGO,触发黑屏冻结异常

相关代码

JS代码

// entryAnimation
const text = document.querySelector(".preloader__text--content");
const strText = text.textContent;
const splitText = strText.split("");
text.textContent = "";

for (let i = 0; i < splitText.length; i++) {
text.innerHTML += "<span>" + splitText[i] + "</span>"
}

let char = 0;
let timer = setInterval(onTick, 50);

function onTick(){
  const span = text.querySelectorAll('span')[char]
  span.classList.add('fade');
  console.log(char)
  char++;
  if(char===splitText.length){
    complete();
    setTimeout(() => {
      preloader.classList.add('hide');
    }, 1500);

    return;
  }
}

function complete(){
  clearInterval(timer);
  timer = null;
}

barba.use(barbaCss);

// barba.hooks.enter(() => {
//   window.scrollTo(0, 0);
// });

barba.init({
  transitions: [
{
      name: 'cover',
      to:{
        namespace: ['home', 'project']
      },
      leave(){},
      enter(){},
    },
    {
      name: 'entry',
      once(){}
    }
  ],
  views: [{
    namespace: 'home',
    beforeEnter(data) {
      preloader.classList.add('hide');
    }
  }]
});

const preloader = document.querySelector(".preloader");

HTML代码

<div class="preloader">
    <h1 class="fs-header-1 fw-demi preloader__text--content">H E N L O</h1>
</div>

CSS代码

.preloader {
    position: fixed;
    transform: translateY(0);
    background-color: var(--clr-neutral-900);
    height: 100vh;
    width: 100%;
    z-index: 2;
    overflow: hidden;
    display: flex;
    justify-content: center;
    align-items: center;
    transition: transform .7s cubic-bezier(0.7, 0, 0.3, 1);
    &.hide{
        transform: translateY(-100%);
    }

    &__text--content {
        overflow: hidden;
        display: block;
        position: absolute;

        span {
            transition: transform 1s cubic-bezier(0.7, 0, 0.3, 1);
            display: inline-block;
            position: relative;
            color: var(--clr-neutral-100);
            transform: translateY(100%);
        }

        span.fade {
            transform: translateY(0%);

        }
    }
}

.entry-once .preloader {
    transform: translateY(0%);
}

.entry-once-active,
.entry-once-active .preloader {
    transition: transform .7s cubic-bezier(0.7, 0, 0.3, 1) 1.3s;
}

.entry-once-to .preloader {
    transform: translateY(100%);
}

.transition {
    position: fixed;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    background-color: var(--clr-neutral-900);
    display: flex;
    align-items: center;
    justify-content: center;
    transform: translateY(-100%);
    z-index: 1;
}

.cover-leave-active,
.cover-enter-active,
.cover-leave-active .transition,
.cover-enter-active .transition {
    transition: transform .7s cubic-bezier(0.7, 0, 0.3, 1);
}

.cover-leave .transition {
    transform: translateY(-100%);
}

.cover-leave-to .transition {
    transform: translateY(0);
}

.cover-enter .transition {
    transform: translateY(0);
}

.cover-enter-to .transition {
    transform: translateY(100%);
}

问题分析

  1. 入场动画仅首次执行:原entry过渡的once()方法只在页面首次加载时触发,从其他页面返回首页时不会重新初始化动画。
  2. 动画状态未重置:返回首页时,preloader的hide类未移除、字符的fade类未清除,导致动画无法重新触发。
  3. 过渡逻辑冲突:beforeEnter中直接添加hide类,强制隐藏preloader,和入场动画执行逻辑冲突引发黑屏。

修复方案

修改后的JS代码

// 封装入场动画逻辑,支持重复调用
function initEntryAnimation() {
  const text = document.querySelector(".preloader__text--content");
  const strText = text.textContent;
  const splitText = strText.split("");
  
  // 重置文本结构与字符状态
  text.textContent = "";
  text.innerHTML = splitText.map(char => `<span>${char}</span>`).join('');

  let char = 0;
  const timer = setInterval(onTick, 50);

  function onTick(){
    const span = text.querySelectorAll('span')[char];
    span.classList.add('fade');
    char++;
    if(char === splitText.length){
      clearInterval(timer);
      setTimeout(() => {
        preloader.classList.add('hide');
      }, 1500);
    }
  }
}

barba.use(barbaCss);

// 全局钩子:进入新页面时自动滚动到顶部
barba.hooks.enter(() => {
  window.scrollTo(0, 0);
});

barba.init({
  transitions: [
    {
      name: 'cover',
      to:{
        namespace: ['project']
      },
      leave() {},
      enter() {}
    },
    {
      name: 'entry',
      to:{
        namespace: ['home']
      },
      // 进入首页时重置并执行动画
      enter() {
        const preloader = document.querySelector(".preloader");
        preloader.classList.remove('hide');
        // 清除字符的fade类,重置动画状态
        document.querySelectorAll('.preloader__text--content span').forEach(span => {
          span.classList.remove('fade');
        });
        initEntryAnimation();
      },
      // 首次加载时执行动画
      once() {
        initEntryAnimation();
      }
    }
  ],
  views: [{
    namespace: 'home',
    // 移除强制隐藏preloader的逻辑
    beforeEnter(data) {}
  }]
});

const preloader = document.querySelector(".preloader");

调整后的CSS(移除冲突样式)

/* 移除Barba自动生成的entry-once相关样式,改用JS控制动画 */
/* .entry-once .preloader {
    transform: translateY(0%);
}

.entry-once-active,
.entry-once-active .preloader {
    transition: transform .7s cubic-bezier(0.7, 0, 0.3, 1) 1.3s;
}

.entry-once-to .preloader {
    transform: translateY(100%);
} */

修复说明

  1. 复用动画逻辑:将入场动画封装成函数,首次加载和返回首页时均可调用。
  2. 重置状态:进入首页前清除preloader的hide类和字符的fade类,确保动画能从头执行。
  3. 拆分过渡规则:让cover过渡仅针对跳转到项目页的场景,entry过渡专门处理进入首页的场景,避免过渡冲突。
  4. 移除强制隐藏:删除beforeEnter中直接隐藏preloader的代码,让动画自然完成后再隐藏。

内容的提问来源于stack exchange,提问作者dzm11

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 17:05:18