【问题标题】:Play / pause Vimeo Video when watcher switches browser tab or minimize browser window当观察者切换浏览器选项卡或最小化浏览器窗口时播放/暂停 Vimeo 视频
【发布时间】:2020-04-18 08:05:50
【问题描述】:

我使用 Vimeo Pro 在 Wordpress 网站上上传视频课程,然后使用 H5P 在视频中添加互动。

为了在 H5P 交互式视频中插入这些 vimeo 视频,我需要使用 Vimeo Pro 的 .mp4 发行版(而不是 Vimeo Iframe)。这是一个例子:

https://player.vimeo.com/external/376040732.sd.mp4?s=a88abddb83ad31962643b6c4dd8270323d80874e&profile_id=165


在网站上

如果我检查我的网站,这是代码:

<div class="h5p-video-wrapper h5p-video hardware-accelerated">

<video src="https://player.vimeo.com/external/376040732.sd.mp4?s=a88abddb83ad31962643b6c4dd8270323d80874e&amp;profile_id=165" webkit-playsinline="" playsinline="" preload="metadata" disableremoteplayback="" class="h5p-video" style="display: block;">
</video>
<div class="h5p-overlay h5p-ie-transparent-background"></div>

</div>

我的请求

当学生切换浏览器选项卡或最小化浏览器窗口时,最简单的暂停视频的方法是什么?感谢您的帮助!

编辑 - 解决方案

如果您在网页中插入一个 H5P 交互式视频,Matt Oestreich 和 Oliver Tacke 的脚本都可以工作。

注意:如果您需要在网页中插入多个 H5P 互动视频,请使用 Matt Oestreich 的脚本。

【问题讨论】:

  • 我继续并想出了如何做到这一点,无论 ID 是什么或有多少......请查看我的更新答案。
  • 我们有!我用解决方案更新了问题。

标签: javascript html css video vimeo


【解决方案1】:


回答:

由于这些答案的字符限制,我不得不清除很多以前的更新。

我设法让它与多个 iframe 一起工作..

唯一的问题是,如果有人播放 2 个不同的视频,然后点击不同的标签(暂停所有视频)然后他们回来,它将播放所有视频...我可以尝试弄清楚解决这个问题,但现在应该这样做。

请务必查看上面更新后的演示网站。

只需将此脚本放在包含您希望使用 Page Visible API 暂停/播放的视频的任何页面上,其余部分应自行处理。

您将需要使用以下内容:

const THE_IFRAME_SELECTOR = 'iframe[id^="h5p-iframe-"]'; // matches all iframes with an id that starts with h5p-iframe-
const THE_VIDEO_SELECTOR = 'video.h5p-video'; // since all videos appear to have the same class you don't need to change this

因此,新脚本应如下所示:

// ====================================================================================================================
// ~~~ SECOND SCRIPT ~~~ 
// THIS WAITS FOR THE IFRAME AND THE VIDEO INSIDE OF THE IFRAME TO BE CREATED BEFORE INITIALIZING THE PAGE VISIBLE API
// ====================================================================================================================
// "Global" variables.
const THE_IFRAME_SELECTOR = 'iframe[id^="h5p-iframe-"]'; // matches all iframes with an id that starts with h5p-iframe-
const THE_VIDEO_SELECTOR = 'video.h5p-video'; // since all videos appear to have the same class you don't need to change this

waitForMultipleElements(document, THE_IFRAME_SELECTOR, () => {
  let ALL_IFRAMES = document.querySelectorAll(THE_IFRAME_SELECTOR);
  ALL_IFRAMES.forEach(FOUND_IFRAME => {
    let FOUND_IFRAME_SELECTOR = `#${FOUND_IFRAME.id}`;
    console.log("FOUND_IFRAME_SELECTOR:", FOUND_IFRAME_SELECTOR)
    waitForElement(document, FOUND_IFRAME_SELECTOR, () => {
      waitForVideoElement(FOUND_IFRAME_SELECTOR, THE_VIDEO_SELECTOR, () => {
        initPageVisibleApi(FOUND_IFRAME_SELECTOR, THE_VIDEO_SELECTOR);
      });
    });
  });
});

function waitForMultipleElements(parentEl, selector, callback) {
  let theInterval = setInterval(() => {
    console.log('still waiting for all elements: ' + selector);
    let elements = parentEl.querySelectorAll(selector);
    if (elements.length) {
      console.log("elements: " + selector + " exist!");
      clearInterval(theInterval);
      callback();
    }
  }, 1000);
}

function waitForVideoElement(iframeSelector, videoElementSelector, callback) {
  let theIframeElement = document.querySelector(iframeSelector);
  let iframeEl = theIframeElement.contentWindow.document;
  waitForElement(iframeEl, videoElementSelector, () => {
    callback()
  });
}

function waitForElement(parentEl, selectorOfElementToWaitFor, callback) {
  let theInterval = setInterval(() => {
    console.log("still waiting for " + selectorOfElementToWaitFor);
    let element = parentEl.querySelector(selectorOfElementToWaitFor);
    if (element) {
      console.log(selectorOfElementToWaitFor + " exists!");
      clearInterval(theInterval);
      callback();
    }
  }, 100);
}

function initPageVisibleApi(iframeSelector, videoSelector) {
  // This is the same code that builds out the Page Visible API
  // event listeners. 
  // The only difference is I wrapped it in a function and added 
  // parameters to make it flexible.
  const iframe = document.querySelector(iframeSelector);
  const innerDoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;
  const videoElement = innerDoc.querySelector(videoSelector);
  // Set the name of the hidden property and the change event for visibility
  var hidden, visibilityChange;
  if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support 
    hidden = "hidden";
    visibilityChange = "visibilitychange";
  } else if (typeof document.msHidden !== "undefined") {
    hidden = "msHidden";
    visibilityChange = "msvisibilitychange";
  } else if (typeof document.webkitHidden !== "undefined") {
    hidden = "webkitHidden";
    visibilityChange = "webkitvisibilitychange";
  }

  // To tell if video has been played yet or not
  let VIDEO_HAS_BEEN_PLAYED = false;
  // If the page is hidden, pause the video;
  // if the page is shown, play the video
  function handleVisibilityChange() {
    if (VIDEO_HAS_BEEN_PLAYED) {
      if (document[hidden]) {
        videoElement.pause();
      } else {
        videoElement.play();
      }
    }
  }
  // Warn if the browser doesn't support addEventListener or the Page Visibility API
  if (typeof document.addEventListener === "undefined" || hidden === undefined) {
    alert("This demo requires a browser, such as Google Chrome or Firefox, that supports the Page Visibility API.");
  } else {
    // Handle page visibility change   
    document.addEventListener(visibilityChange, handleVisibilityChange, false);

    // When the video pauses, set the title.
    // This shows the paused
    const defaultTitle = document.title;
    videoElement.addEventListener("pause", function () {
      document.title = 'Paused - ' + defaultTitle;
    }, false);
    // When the video plays, set the title.
    videoElement.addEventListener("play", function () {
      VIDEO_HAS_BEEN_PLAYED = true;
      document.title = 'Playing - ' + defaultTitle;
    }, false);
  }
}
// ====================================================================================================================
// ---------- END SECOND SCRIPT ------------
// ====================================================================================================================

【讨论】:

  • Matt,我在我的实时网站上为您的脚本发布了一个专门的网页:sontraining.com/play-pause-h5p-video-iframe 您能否在您的回答中更新我上面的网站链接? (这是因为该网页将来将无法使用)谢谢
【解决方案2】:

如果您想修改 H5P 的功能而不是“hack”,您可能应该使用自定义的常用方法。它通常使事情变得容易得多。详情请见https://h5p.org/wordpress-customization

无论如何,如果您想在 WordPress 主题中添加一些脚本,应该这样做:

// This will only work for the first H5P IV found on the page!
// Since I don't know your environment, let's stick with ES5
var hidden, visibilityChange;
var interactiveVideo;
var currentState = -1;
var restartVideo = false;

/**
 * Handle change of page visibility.
 */
function handleVisibilityChange() {
  if ( ! interactiveVideo ) {
    return;
  }

  if ( document[hidden] ) {
    // There's nothing to do if the video isn't playing at all
    if ( 1 !== currentState ) {
      return;
    }

    restartVideo = true;
    interactiveVideo.pause();
  }
  else {
    // Restart if video was playing before
    if ( ! restartVideo ) {
      return;
    }

    restartVideo = false;
    interactiveVideo.play();
  }
}

/**
 * Add the stop/resume feature to video.
 * @param {Window} contentWindow Content Window containing H5P.
 */
function addVideoStopResume( contentWindow ) {
  // Set the name of the hidden property and the change event for visibility
  if ( 'undefined' !== typeof document.hidden ) { // Opera 12.10 and Firefox 18 and later support
    hidden = 'hidden';
    visibilityChange = 'visibilitychange';
  }
  else if ( 'undefined' !== typeof document.msHidden ) {
    hidden = 'msHidden';
    visibilityChange = "msvisibilitychange";
  }
  else if ( 'undefined' !== typeof document.webkitHidden ) {
    hidden = 'webkitHidden';
    visibilityChange = 'webkitvisibilitychange';
  }

  // Get first instance of H5P.interactiveVideo -- will not work properly if there are more instances of IV!
  interactiveVideo = interactiveVideo || contentWindow.H5P.instances.filter( function ( instance ) {
    return instance.libraryInfo && 'H5P.InteractiveVideo' === instance.libraryInfo.machineName;
  })[0];

  if ( ! interactiveVideo ) {
    return;
  }

  // Remember current video state
  interactiveVideo.video.on('stateChange', function (state) {
    currentState = state.data;
  });

  if ( 'undefined' === typeof document.addEventListener || undefined === hidden ) {
    console.warn( 'The video stop/resume feature requires a browser, such as Google Chrome or Firefox, that supports the Page Visibility API.' );
  }
  else {
    // Handle page visibility change
    document.addEventListener( visibilityChange, handleVisibilityChange, false );
  }
}

/**
 * Initialize stop/resume feature.
 */
function initVideoStopResume() {
  var iframes = document.getElementsByTagName( 'iframe' );
  var i;
  var contentWindow;

  // Add EventListener if H5P content is present
  if ( 'complete' === document.readyState ) {
    for ( i = 0; i < iframes.length; i++ ) {

      // Skip non H5P iframes and remote iframes
      if ( ! iframes[i].classList.contains( 'h5p-iframe' ) &&
        (
          0 !== iframes[i].src.indexOf( window.location.origin ) ||
                    -1 === iframes[i].src.indexOf( 'action=h5p_embed' )
        )
      ) {
        continue;
      }

      // Edge needs to wait for iframe to be loaded, others don't
      contentWindow = iframes[i].contentWindow;
      if ( contentWindow.H5P ) {
        addVideoStopResume( contentWindow );
      }
      else {
        iframes[i].addEventListener( 'load', function () {
          contentWindow = this.contentWindow;
          addVideoStopResume( contentWindow );
        });
      }
    }
  }
}

// Amend content functionality when H5P content has loaded
if ( 'complete' === document.readyState ) {
  initVideoStopResume();
}
else {
  document.onreadystatechange = function () {
    if ( 'complete' === document.readyState ) {
      initVideoStopResume();
    }
  };
}

脚本将等待页面加载完毕,然后遍历页面上的每个 H5P iframe 以搜索交互式视频。然后,它会添加所需的功能来停止/恢复视频可见性的变化。

请注意,如果您在其他内容类型(例如 H5P 列)中使用 IV,此解决方案将无法正常工作。此外,它仅适用于页面上的第一个交互式视频。添加这些功能将需要更多代码,并且没有被要求。

【讨论】:

  • 问题是 document.readyState IS complete,但 H5P iframes 尚未加载。所以这行不通。我已经试过了。我什至尝试使用 iframe 加载事件,但这也不起作用,因为 iframes 在将“虚拟”文档添加到 DOM 后立即加载它们(这就是为什么我还要等待实际的视频元素在 iframe Check out this exaggerated demo 内创建 - 文档已准备好,但 iframe/视频还没有.. code is here
  • 我不依赖 iframe 来完全加载。我没有使用 DOM 的视频实例,而是 H5P.Video 的实例,据我所知,只要 iframe 可用,它就应该始终可用。
  • 是的,我知道你不是……这就是问题所在。由于 H5P 似乎以编程方式将视频元素添加到 iframe,因此您之前点击了 document.readystate === 'complete' 事件视频已经准备好了..否则我会使用它..为了安全起见,我检查元素以确保它们首先存在(其中的 iframe 和视频),然后再分配 Page Visible API。无论如何,这并不重要,你不必相信我的话,但我已经用 OP 盲目地测试了 2 天。
  • 我刚刚在我的“夸张的演示”中测试了这个,这个代码对我不起作用。虽然,它似乎适用于 OP(我对 Wordpress 或 H5P 完全不熟悉,所以我不知道加载必要的 DOM 元素是否需要 5ms 或 5000ms - 你似乎更了解 WP/H5P比我更有效).. 我认为最好在分配 Page Visible API 之前确保这些元素 100% 存在..(似乎 iframe 在 DOM 中为 OP 的“就绪状态完成”)
  • 如果在 H5P.video 实例上调用 play() 和 pause() 真的需要等待视频 DOM 元素加载吗?
猜你喜欢
  • 2021-12-09
  • 2014-04-04
  • 2013-03-30
  • 2018-08-09
  • 1970-01-01
  • 2014-02-20
  • 1970-01-01
  • 1970-01-01
  • 2021-02-19
相关资源
最近更新 更多