CSS3-动画元素如果在视口中可见(页面滚动)

Posted

技术标签:

【中文标题】CSS3-动画元素如果在视口中可见(页面滚动)【英文标题】:CSS3-Animate elements if visible in viewport (Page Scroll) 【发布时间】:2015-02-12 06:22:48 【问题描述】:

我已经在我的 html 页面上的各种 div 元素中添加了 CSS 动画。但是所有的动画同时播放并且我在页面底部看不到动画。我怎样才能让它们在我滚动时播放在页面下方?

【问题讨论】:

使用 JS,如果元素在 在视口中,则为所有元素添加一个类。该类应该触发 CSS3 动画。 【参考方案1】:

使用 IntersectionObserver API

IntersectionObserver API 提供了一种异步观察目标元素与祖先元素或***文档视口的交集变化的方法。

这是一个当元素在视口中时触发classList toggle 的示例:

const inViewport = (entries, observer) => 
  entries.forEach(entry => 
    entry.target.classList.toggle("is-inViewport", entry.isIntersecting);
  );
;

const Obs = new IntersectionObserver(inViewport);
const obsOptions = ; //See: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options

// Attach observer to every [data-inviewport] element:
const ELs_inViewport = document.querySelectorAll('[data-inviewport]');
ELs_inViewport.forEach(EL => 
  Obs.observe(EL, obsOptions);
);
[data-inviewport]  /* THIS DEMO ONLY */
  width:100px; height:100px; background:#0bf; margin: 150vh 0; 


/* inViewport */

[data-inviewport="scale-in"]  
  transition: 2s;
  transform: scale(0.1);

[data-inviewport="scale-in"].is-inViewport  
  transform: scale(1);


[data-inviewport="fade-rotate"]  
  transition: 2s;
  opacity: 0;

[data-inviewport="fade-rotate"].is-inViewport  
  transform: rotate(180deg);
  opacity: 1;
Scroll down...
<div data-inviewport="scale-in"></div>
<div data-inviewport="fade-rotate"></div>

观察者选项

要定义另一个父引用元素,请使用可观察选项对象内的root 选项。您还可以选择rootMargin 和超级有用的threshold 选项

const obsOptions = 
  // Default is null (Browser viewport). Set a specific parent element:
  root: document.querySelector('#someSpecificParent'),
  // add 40px inner "margin" area at which the observer starts to calculate:
  rootMargin: '40px', 
  // Default is 0.0 meaning the callback is called as soon 1 pixel is inside the viewport.  
  // Set to 1.0 to trigger a callback when 100% of the target element is inside the viewport,   
  // or i.e: 0.5 when half of the target element is visible:
  threshold: 0.5, 
;

查看另一个interesting use case that uses the IntersectionObserver API's threshold 选项。

补充阅读:

w3.org developer.mozilla.org caniuse.comIE浏览器

使用 native IntersectionObserver API 是解决此问题的最高效的方法。 如果您想了解我们过去是如何解决类似需求的,请以 this answer with a small custom plugin 为例。

【讨论】:

是的,我包含了插件 @Ajith 您的目标是类为 $(".box") 的元素,但在您的 HTML 中您没有定义此类。我已经添加了这些类,但到目前为止还没有动画......没有时间去处理你的整个代码......再次查看我的示例并从那里开始。试着找出问题所在。我认为这是一些定位问题......但我不确定。 @RokoC.Buljan 你知道如何删除这个类,这样当你往回滚动时它会再次触发动画? @probablybest 只需使用$(this).toggleClass("triggeredCSS3", !!px ); jsfiddle.net/RokoCB/tw6g2oeu/16 在某些网络浏览器上$(window).height() 返回文档高度而不是视口高度,除非您添加严格的文档类型,如here 所述。也许这对某人有帮助,因为我花了一些时间才弄清楚。【参考方案2】:

仍然是 javascript,但在这个版本中,您不需要监听滚动事件。 速度和性能比每次检查对象是否在视口中要好得多。

检查这个: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

使用Intersection Observer,您可以在元素可见时定义回调。

选项:root: null threshold: 0.3

function callbackFunc(entries, observer)

  entries.forEach(entry => 
    var txt = entry.target.id + " visibility: " + entry.isIntersecting;
    
    document.getElementById('log').appendChild(document.createTextNode(txt));
    document.getElementById('log').appendChild(document.createElement("br"));
  );


let options = 
    root: null,
    rootMargin: '0px',
    threshold: 0.3
  ;

let observer = new IntersectionObserver(callbackFunc, options);

observer.observe(document.getElementById('firstBlock'));
observer.observe(document.getElementById('secondBlock'));
#firstBlock 
  width: 50vw;
  height: 80vh;
  background: red;


#secondBlock 
  width: 50vw;
  height: 80vh;
  background: blue;


#log 
  width: 200px;
  height: 80vh;
  position: fixed;
  right: 0px;
  top: 10px;
  overflow: auto;
First Block:
<div id='firstBlock'> </div>
<br><br><br>
Second Block:
<div id='secondBlock'> </div>
<div id='log'>Log: </div>

【讨论】:

我觉得现在应该接受这个答案。较旧的示例仍然有效,但已经过时了。 这应该是正确的答案并展示了良好的做法,避免了数千次执行功能。 当您滚动回第一个块时,删除第二个块可见性文本怎么样?看起来应该不会太难…… 看起来它还不是 100% 精确,即使我看到第一个块的某些部分,第一个块的可见性也会变成错误【参考方案3】:

另一种方法是使用滚动事件监听器

document.addEventListener("DOMContentLoaded", function(event) 
    document.addEventListener("scroll", function(event) 
        const animatedBoxes = document.getElementsByClassName("animated-box");
        const windowOffsetTop = window.innerHeight + window.scrollY;

        Array.prototype.forEach.call(animatedBoxes, (animatedBox) => 
            const animatedBoxOffsetTop = animatedBox.offsetTop;

            if (windowOffsetTop >= animatedBoxOffsetTop) 
                addClass(animatedBox, "fade-in");
            
        );
    );
);

function addClass(element, className) 
    const arrayClasses = element.className.split(" ");
    if (arrayClasses.indexOf(className) === -1) 
        element.className += " " + className;
    
.animated-box 
  width: 150px;
  height: 150px;
  margin-top: 100vh;
  background: blue;


.fade-in 
    -webkit-animation: fade-in 1.2s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
            animation: fade-in 1.2s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;


 @-webkit-keyframes fade-in 
  0% 
    -webkit-transform: translateY(50px);
            transform: translateY(50px);
    opacity: 0;
  
  100% 
    -webkit-transform: translateY(0);
            transform: translateY(0);
    opacity: 1;
  

@keyframes fade-in 
  0% 
    -webkit-transform: translateY(50px);
            transform: translateY(50px);
    opacity: 0;
  
  100% 
    -webkit-transform: translateY(0);
            transform: translateY(0);
    opacity: 1;
  
<div>
  Start scrolling down...
  
  <div class="animated-box">

  </div>
        
  <div class="animated-box">
        
  </div>

  <div class="animated-box">
  
  </div>
  
  <div class="animated-box">
  
  </div>
  
  <div class="animated-box">
  
  </div>
</div>

【讨论】:

以上是关于CSS3-动画元素如果在视口中可见(页面滚动)的主要内容,如果未能解决你的问题,请参考以下文章

页面滚动时 CSS3 动画打破固定定位

scrollreveal(页面滚动显示动画插件支持手机)

kissui.scrollanim页面滚动动画库插件

将 CSS3 动画/变换与滚动事件联系起来

前端必知:如何判断元素出现在视口内(性能优化涉及)

前端必知:如何判断元素出现在视口内(性能优化涉及)