HTML5 和 Javascript 仅在可见时播放视频

Posted

技术标签:

【中文标题】HTML5 和 Javascript 仅在可见时播放视频【英文标题】:HTML5 and Javascript to play videos only when visible 【发布时间】:2014-02-05 11:26:03 【问题描述】:

我有兴趣设置一个包含多个视频剪辑的 html 页面,以便每个视频剪辑仅在可见时播放,然后在不可见时暂停。

我已经找到this great example 了解如何使用一个剪辑来实现这一点,但我无法修改代码以使用多个剪辑。也许我需要将此代码转换为函数以便于重用?

这是我到目前为止所拥有的(上面链接的 JS Bin 修改为 2 个剪辑而不是一个)。

此代码似乎只适用于两个剪辑中的一个。

<!DOCTYPE html>
<html>
    <!--   Created using jsbin.com   Source can be edited via http://jsbin.com/ocupor/1/edit
    -->
    <head>
        <meta charset=utf-8 />
        <title>JS Bin</title>
        <style>
            #right 
                position: absolute;
                top: 2000px;
            
            #video1 
                position: absolute;
                left: 2000px;
                top: 2000px;
            
            #video2 
                position: absolute;
                left: 2000px;
                top: 3000px;
            

        </style>

        <style id="jsbin-css">
        </style>
    </head>
    #
    <body style="width: 4000px; height: 4000px;">
        <div id="info"></div>
        <div id="down">
            scroll down please...
        </div>
        <div id="right">
            scroll right please...
        </div>
        <video id="video1">
            <source src="http://video-js.zencoder.com/oceans-clip.mp4"/>

        </video>
        <script>
            var video = document.getElementById('video1'), fraction = 0.8;

            function checkScroll() 
                var x = video.offsetLeft, y = video.offsetTop, w = video.offsetWidth, h = video.offsetHeight, r = x + w, //right
                b = y + h, //bottom
                visibleX, visibleY, visible;

                visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
                visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));

                visible = visibleX * visibleY / (w * h);

                if (visible > fraction) 
                    video.play();
                 else 
                    video.pause();
                
            

            checkScroll();
            window.addEventListener('scroll', checkScroll, false);
            window.addEventListener('resize', checkScroll, false);
        </script>

        <video id="video2">
            <source src="http://video-js.zencoder.com/oceans-clip.mp4"/>

        </video>
        <script>
            var video = document.getElementById('video2'), fraction = 0.8;

            function checkScroll() 
                var x = video.offsetLeft, y = video.offsetTop, w = video.offsetWidth, h = video.offsetHeight, r = x + w, //right
                b = y + h, //bottom
                visibleX, visibleY, visible;

                visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
                visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));

                visible = visibleX * visibleY / (w * h);

                if (visible > fraction) 
                    video.play();
                 else 
                    video.pause();
                
             checkScroll();
            window.addEventListener('scroll', checkScroll, false);
            window.addEventListener('resize', checkScroll, false);

        </script>

    </body>
</html>

【问题讨论】:

【参考方案1】:

好吧,我想,应该是这样的:

var videos = document.getElementsByTagName("video");

function checkScroll() 
    var fraction = 0.8; // Play when 80% of the player is visible.

    for(var i = 0; i < videos.length; i++) 

        var video = videos[i];

        var x = video.offsetLeft, y = video.offsetTop, w = video.offsetWidth, h = video.offsetHeight, r = x + w, //right
            b = y + h, //bottom
            visibleX, visibleY, visible;

            visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
            visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));

            visible = visibleX * visibleY / (w * h);

            if (visible > fraction) 
                video.play();
             else 
                video.pause();
            

    



window.addEventListener('scroll', checkScroll, false);
window.addEventListener('resize', checkScroll, false);

【讨论】:

我得到一个“未定义的分数”ReferenceError,但是一旦我添加了定义就可以完美运行:fraction = 0.8; 是的,分数只需要在某个地方定义——不过答案很好:) 这对我不起作用,但作为参考很有用。【参考方案2】:

使用isInViewport 插件和jQuery,这是我的任务代码

$('video').each(function()
    if ($(this).is(":in-viewport")) 
        $(this)[0].play();
     else 
        $(this)[0].pause();
    
)

【讨论】:

我正在尝试让这段代码为我工作,它要简单得多。不过,我遇到了麻烦。由于我已切换到基于 wordpress 的网站,因此我的视频遵循以下示例格式:class="wp-video-shortcode" id="video-1115-1" 有什么方法可以修改 [0] 以使用 video-XXXX-X 格式? 我们可以申请 Vimeo 声明它在 iframe 下运行吗?怎么做? 投反对票,因为 OP 没有指定 jQuery 标签。可以预料,这个问题是针对 vanilla javascript 的。 @KalleH.Väravas 在 2014 年发布此答案时并非如此,而且这些深奥的规则只会阻碍贡献。【参考方案3】:

以上方法似乎都不适合我,但我终于找到了一种方法:你需要the visible plugin,这里有这段小代码:

$(window).scroll(function() 
    $('video').each(function() 
        if ($(this).visible(true)) 
            $(this)[0].play();
         else 
            $(this)[0].pause();
        
    )
);

这将允许任何视频仅在进入视口时播放。通过将visible( true ) 替换为visible() ,您可以将其设置为仅在整个视频 DOM 元素在视口中时播放。

【讨论】:

谢谢你! 也谢谢你!正是我所需要的具有多个全屏视频部分的滚动页面,这些部分在 Chrome 和 Mac Safari 上没有表现。使用这个插件和上面的代码修复了everything。再次感谢! 如果你们不想使用插件,请查看this 回答。 您的解决方案效果最好!谢谢【参考方案4】:

需要检查视频在滚动过程中是否可见。

 $(window).scroll(function() 
    $('video').each(function()
        if ($(this).is(":in-viewport")) 
            $(this)[0].play();
         else 
            $(this)[0].pause();
        
    )
);

【讨论】:

【参考方案5】:

你们都需要与时俱进并使用 IntersectionObserver(以及适当的 polyfill 或 babeifyl)。此脚本将在页面滚动进/出视图时播放/暂停页面上的所有视频。轰隆隆。

<script crossorigin="anonymous" src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver%2CIntersectionObserverEntry"></script> 
<script>
    let video = document.querySelector('video');
    let isPaused = false;
    let observer = new IntersectionObserver((entries, observer) =>  
        entries.forEach(entry => 
        if(entry.intersectionRatio!=1  && !video.paused)
            video.pause();
            isPaused = true;
        
        else if(isPaused) 
            video.play(); 
            isPaused=false
        );
    , threshold: 1);
    observer.observe(video);
</script>

source

【讨论】:

有点好奇isPaused 标志在做什么。它是某种性能标志,还是效果所必需的? 看起来应该去掉。 是的,可以像if (entry.intersectionRatio !== 1) video.pause() else video.play()一样简单 css-tricks.com/… - 回头看看视频是否已自动暂停,【参考方案6】:

老问题,但只是想加两分钱,我最初从上面的 jQuery 代码开始,但在实现时遇到了一些问题。此解决方案应该适用于多个视频,并且还可以防止用户暂停视频并尝试滚动并重新开始的问题:

<script>
    var videoList = [];
    var scrollPauseList = [];
    var clickedPauseList = [];
</script>
<script>    
    var myScrollFunc = function() 

        $(".video-js").each(function() 

            var inView = $(this).is(":in-viewport");
            var isPaused = $(this)[0].player.paused();
            var playerIdx = videoList.indexOf(this.id);
            var scrollPaused = scrollPauseList[playerIdx];
            var clickPaused = clickedPauseList[playerIdx];

            if (inView)                        
                var hasEnded = $(this)[0].player.ended();
                var curTime = $(this)[0].player.currentTime();
                var hasStarted = curTime > 0;
                if(hasStarted && !hasEnded && !clickPaused)
                
                    scrollPauseList[playerIdx] = false;
                    $(this)[0].player.play();
                
             else if(!isPaused)                       
                scrollPauseList[playerIdx] = true;
                $(this)[0].player.pause();
            
        );
    ;      

    $(window).scroll(myScrollFunc);     
</script>   

<video  
  class="video-js" controls></video>

<script>
$(".video-js").each(function() 
        videoList[videoList.length] = this.id;
        scrollPauseList[scrollPauseList.length] = false;
        clickedPauseList[scrollPauseList.length] = false;
    ); 

for(var i = 0; i < videoList.length; i++)

    var playerID = videoList[i];
    var player = videojs(playerID);

    player.on('pause', function() 
        var pID = videoList.indexOf(this.id());

        if(!scrollPauseList[pID])
        
            clickedPauseList[pID] = true;
            scrollPauseList[pID] = false;
        
        else
        
            clickedPauseList[pID] = false;
            scrollPauseList[pID] = false;
        
    );
       
</script>

我已经清理了一些东西,并且我正在使用 video-js,因此您可能需要对其进行一些修改以使您的实现正常工作。

【讨论】:

【参考方案7】:

使用 jQuery、isInViewport 和 Coffeescript,我的完整解决方案如下所示:

$(window).scroll ->
  $('video:in-viewport').each -> $(@)[0].play()
  $('video:not(:in-viewport)').each -> $(@)[0].pause()

【讨论】:

【参考方案8】:

尝试了许多解决方案,唯一部分工作的是下面发布的一个。问题是页面上有3个视频,第二个和第三个基本上由第一个控制。

所以他们在页面加载时开始播放(虽然他们应该在视口中播放)并且在第一次暂停时暂停,有什么建议可以处理多个视频吗?

尝试使用getElementById 但没有用,还尝试了 jquery 插件但没有好的结果。

这里有 www 页面,您可以在其中查看发生的情况以及所有源代码。

http://185.197.128.183/~monompro/

window.onload = function() 

    var videos = document.getElementsByTagName("video"),
        fraction = 0.8;

    function checkScroll() 

        for (var i = 0; i < videos.length; i++) 

            var video = videos[i];

            var x = video.offsetLeft,
                y = video.offsetTop,
                w = video.offsetWidth,
                h = video.offsetHeight,
                r = x + w, //right
                b = y + h, //bottom
                visibleX, visibleY, visible;

            visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
            visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));

            visible = visibleX * visibleY / (w * h);

            if (visible > fraction) 
                video.play();
             else 
                video.pause();
            

        

    

    window.addEventListener('scroll', checkScroll, false);
    window.addEventListener('resize', checkScroll, false);

【讨论】:

【参考方案9】:

这就是我仅在用户滚动到视频时才设法播放视频的方法。 我使用了 IsInViewport 插件。 希望对您有用!

$(window).scroll(function() 

    var video = $('.yourvideo');

    $(video).each(function()

        if(video.is(':in-viewport'))

            video[0].play();

            video.removeClass('yourvideo');
            //I removed class to stop repeating the action ".play()" when it is scrolled again.
        
    );
);

【讨论】:

【参考方案10】:

如果其他人遇到这个问题,我无法在我的 WordPress 网站上使用 Saike 的解决方案,因为视频是自动嵌入的(MediaElement 播放器)。但是,qwazix 的解决方案经过了一些修改。这是与IsInView plugin 一起使用的jQuery 代码。这是我的包含脚本(放在我的主题文件夹中footer.php 的末尾)。

<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="js/isInViewport.min.js" type="text/javascript"></script>
<script src="js/scrollview.min.js" type="text/javascript"></script>

还有 jQuery 代码(修改 400 到你喜欢的容忍度)

  $(function() 
      $(window).scroll(function() 
          $('.wp-video-shortcode').each(function() 
              var str = $(this).attr('id');
              var arr = str.split('_');
              typecheck = arr[0];
              if ($(this).is(":in-viewport( 400 )") && typecheck == "mep") 
                  mejs.players[$(this).attr('id')].media.play();
               else if (typecheck == "mep") 
                  mejs.players[$(this).attr('id')].media.pause();
              
          );
      );
  );

我对这段代码的唯一问题是它确实在滚动时重新启动视频剪辑,即使用户暂停。在我的网站上不是一个破坏交易的问题。以下是实际代码:Ultrasoundoftheweek.com

【讨论】:

【参考方案11】:

正如here 解释的那样,offsetTop/offsetLeft/等。方法比新的getBoundingClientRect 方法更慢且更容易出错。下面是一些工作代码,可以播放在视口中部分可见的任何视频:

function playVisibleVideos() 
  document.querySelectorAll("video").forEach(video => elementIsVisible(video) ? video.play() : video.pause());


function elementIsVisible(el) 
  let rect = el.getBoundingClientRect();
  return (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));


let playVisibleVideosTimeout;
window.addEventListener("scroll", () => 
  clearTimeout(playVisibleVideosTimeout);
  playVisibleVideosTimeout = setTimeout(playVisibleVideos, 100);
);

window.addEventListener("resize", playVisibleVideos);
window.addEventListener("DOMContentLoaded", playVisibleVideos);

setTimeout 的内容可确保在用户滚动时,playVisibleVideos() 函数的调用频率不会超过每 100 毫秒一次(因此不会导致延迟)。

请注意,对于大多数人来说,似乎@Tristanisginger's answer 使用更现代的IntersectionObserver 方法可能比这种方法更好。

【讨论】:

【参考方案12】:

如果您正在寻找一个没有任何依赖关系的简单解决方案,这里是:

 let video = document.getElementById('video')

 function playVideoOnScroll () 
    const threshold = 300 //px above the video to start playing
    let offset = video.getBoundingClientRect().top
    if (offset < threshold) 
      demoVideo.play()
     else 
      demoVideo.pause()
    
  

  window.addEventListener('scroll', playVideoOnScroll, false)
  window.addEventListener('resize', playVideoOnScroll, false)

【讨论】:

以上是关于HTML5 和 Javascript 仅在可见时播放视频的主要内容,如果未能解决你的问题,请参考以下文章

仅在视频播放时进行视频控制 (html5)

如何仅在某些页面上隐藏我的左侧导航菜单

在 HTML5 画布上工作时出现 JavaScript (JQuery) 问题

我正在扩展页眉和页脚,但是当我在页脚中传递数据时,它仅在主页上可见,在其他页面上不可见

HTML5 视频无法仅在 Chrome 中播放

HTML5 日期选择器仅在第二次点击时打开