滚动到视图时开始动画

Start animation when scrolled into view

我的网站上有进度条来直观地显示百分比,它有一个动画让它们随着页面滚动。但是,此动画会在页面加载后立即启动。

我的问题是进度条在页面下方,加载页面时无法及时看到动画。有什么方法可以让元素在视图中时才开始播放动画吗?

@keyframes example {
  0% {
    width: 0%;
  }
  10% {
    width: 0%;
  }
  100% {
    max-width: 100%;
  }
}

.bara1 {
  border-radius: 1px 25px 25px 1px;
  padding: 16px;
  background-color: #7ac1cf;
  position: relative;
  margin-bottom: -32px;
  z-index: 2;
  animation-name: example;
  animation-duration: 6s;
}

.barb1 {
  border-radius: 1px 25px 25px 1px;
  padding: 16px;
  background-color: #e3e3e3;
  position: relative;
  margin-bottom: 8px;
  z-index: 1;
}

.textbox {
  position: absolute;
  color: #ffffff;
  margin-top: -12px;
}
<div class="bara1" style="width: 92%">
  <div class="textbox"><b>text</b>&nbsp;&nbsp;
    <font size="2px" color="#def7fc">92%</font>
  </div>
</div>
<div class="barb1"></div>

<div class="bara1" style="width: 78%">
  <div class="textbox"><b>text</b>&nbsp;&nbsp;
    <font size="2px" color="#def7fc">78%</font>
  </div>
</div>
<div class="barb1"></div>

<div class="bara1" style="width: 56%">
  <div class="textbox"><b>text</b>&nbsp;&nbsp;
    <font size="2px" color="#def7fc">56%</font>
  </div>
</div>
<div class="barb1"></div>

<div class="bara1" style="width: 40%">
  <div class="textbox"><b>text</b>&nbsp;&nbsp;
    <font size="2px" color="#def7fc">40%</font>
  </div>
</div>
<div class="barb1"></div>

<div class="bara1" style="width: 31%">
  <div class="textbox"><b>text</b>&nbsp;&nbsp;
    <font size="2px" color="#def7fc">31%</font>
  </div>
</div>
<div class="barb1"></div>

您需要使用 Intersection Observer API。基本上只有当栏开始可见时才会添加 class(在下面的示例中,我将其命名为 class animate)。 我在你的 HTML 代码中添加了一个占位符来模拟栏前的内容,并将 animation-name: example.bara1 移动到 .bara1.animate

const observer = new IntersectionObserver(intersections => {
  intersections.forEach(({
    target,
    isIntersecting
  }) => {
    target.classList.toggle('animate', isIntersecting);
  });
}, {
  threshold: 0
});

document.querySelectorAll('.bara1').forEach(div => {
  observer.observe(div);
});
#placeholder {
  height: 100vh;
  display: grid;
  place-items: center;
  font-size: 2rem;
  border: 1px solid;
}

@keyframes example {
  0% {
    width: 0%;
  }

  10% {
    width: 0%;
  }

  100% {
    max-width: 100%;
  }
}

.bara1 {
  border-radius: 1px 25px 25px 1px;
  padding: 16px;
  background-color: #7ac1cf;
  position: relative;
  margin-bottom: -32px;
  z-index: 2;
  animation-duration: 6s;
}

.bara1.animate {
  animation-name: example;
}

.barb1 {
  border-radius: 1px 25px 25px 1px;
  padding: 16px;
  background-color: #e3e3e3;
  position: relative;
  margin-bottom: 8px;
  z-index: 1;
}

.textbox {
  position: absolute;
  color: #ffffff;
  margin-top: -12px;
}
<section id="placeholder">This is a placeholder</section>

<div class="bara1" style="width: 92%">
  <div class="textbox"><b>text</b>&nbsp;&nbsp;
    <font size="2px" color="#def7fc">92%</font>
  </div>
</div>
<div class="barb1"></div>

<div class="bara1" style="width: 78%">
  <div class="textbox"><b>text</b>&nbsp;&nbsp;
    <font size="2px" color="#def7fc">78%</font>
  </div>
</div>
<div class="barb1"></div>

<div class="bara1" style="width: 56%">
  <div class="textbox"><b>text</b>&nbsp;&nbsp;
    <font size="2px" color="#def7fc">56%</font>
  </div>
</div>
<div class="barb1"></div>

<div class="bara1" style="width: 40%">
  <div class="textbox"><b>text</b>&nbsp;&nbsp;
    <font size="2px" color="#def7fc">40%</font>
  </div>
</div>
<div class="barb1"></div>

<div class="bara1" style="width: 31%">
  <div class="textbox"><b>text</b>&nbsp;&nbsp;
    <font size="2px" color="#def7fc">31%</font>
  </div>
</div>
<div class="barb1"></div>

您可以通过在滚动到视图中时向周围容器添加一个 class 来解决此问题,并仅为该 class(分别是其子项)定义动画。您可以在 scroll 事件的事件处理程序中调用该检查。

window.addEventListener('scroll', function() {
  if (isScrolledIntoView(container)) {
    container.classList.add('inView');
  }
});
.inView .bara1 {
  animation-name: example;
  animation-duration: 6s;
}

为此,有必要定义一个函数来检查元素是否可见:

function isScrolledIntoView(elem) {
  var rect = elem.getBoundingClientRect();
  var elemTop = rect.top;
  var elemBottom = rect.bottom;

  var isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
  
  return isVisible;
}

此外,重要的是省略所有 .bara1 的内联样式 (width),而是使用 JavaScript 设置它们,例如使用 [=20] 中的文本=] 元素。否则会出现闪烁,这是由动画重置的完整(内联)宽度引起的。

const bars = document.querySelectorAll('.bara1');
    
for (let i = 0; i < bars.length; i++) {
  const percent = bars[i].querySelector('font').textContent;
  bars[i].style.width = percent;
}    

工作示例:

let container = document.querySelector('#container');

function isScrolledIntoView(elem) {
  var rect = elem.getBoundingClientRect();
  var elemTop = rect.top;
  var elemBottom = rect.bottom;

  var isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
  
  return isVisible;
}

window.addEventListener("scroll", function() {
  if (isScrolledIntoView(container)) {
    container.classList.add('inView');

    const bars = document.querySelectorAll('.bara1');
    
    for (let i = 0; i < bars.length; i++) {
      const percent = bars[i].querySelector('font').textContent;
      bars[i].style.width = percent;
    }
  }
});
#placeholder {
  height: 500px;
  border: 1px solid black;
}

@keyframes example {
  0% {
    width: 0%;
  }
  10% {
    width: 0%;
  }
  100% {
    max-width: 100%;
  }
}

.bara1 {
  width: 0;
  border-radius: 1px 25px 25px 1px;
  padding: 16px;
  background-color: #7ac1cf;
  position: relative;
  margin-bottom: -32px;
  z-index: 2;
}

.inView .bara1 {
  animation-name: example;
  animation-duration: 6s;
}

.barb1 {
  border-radius: 1px 25px 25px 1px;
  padding: 16px;
  background-color: #e3e3e3;
  position: relative;
  margin-bottom: 8px;
  z-index: 1;
}

.textbox {
  position: absolute;
  color: #ffffff;
  margin-top: -12px;
}
<div id="placeholder">placeholder</div>

<div id="container">
  <div class="bara1">
    <div class="textbox"><b>text</b>&nbsp;&nbsp;
      <font size="2px" color="#def7fc">92%</font>
    </div>
  </div>
  <div class="barb1"></div>

  <div class="bara1">
    <div class="textbox"><b>text</b>&nbsp;&nbsp;
      <font size="2px" color="#def7fc">78%</font>
    </div>
  </div>
  <div class="barb1"></div>

  <div class="bara1">
    <div class="textbox"><b>text</b>&nbsp;&nbsp;
      <font size="2px" color="#def7fc">56%</font>
    </div>
  </div>
  <div class="barb1"></div>

  <div class="bara1">
    <div class="textbox"><b>text</b>&nbsp;&nbsp;
      <font size="2px" color="#def7fc">40%</font>
    </div>
  </div>
  <div class="barb1"></div>

  <div class="bara1">
    <div class="textbox"><b>text</b>&nbsp;&nbsp;
      <font size="2px" color="#def7fc">31%</font>
    </div>
  </div>
  <div class="barb1"></div>
</div>