У нас есть сайт Wordpress, который использует пользовательский блок контента для своего баннера героя. У нас есть кнопка, настроенная внутри героя, которая запускает видео с Youtube или Vimeo в Iframe, отображаемом в модальном окне. Однако, если вы сконфигурированы для использования видео Vimeo, при нажатии в любом месте страницы видео VIMEO не запустится. Вместо этого только звук из видео начинает воспроизводиться. Вы можете включить / выключить звук, щелкнув в любом месте страницы (вместо нажатия на кнопку). Кроме того, все вкладки в главной навигации сайта становятся неактивными. Таким образом, вы не можете использовать основную навигацию для перехода с этой страницы. Таким образом, конечный пользователь застревает, слушая аудио из видео, которое он не может видеть, и навигация не доступна.
Вы можете увидеть ошибку в действии, используя Chrome на нашем промежуточном сайте: https://stgkipp.wpengine.com/brand/
Пример страницы с ошибкой
Страница основана на шаблоне, который использует блок Hero, настроенный в WP-Admin. Он имеет кнопку, которая настроена для запуска видео с Youtube или Vimeo. Когда он настроен для ссылки на видео из Vimeo, мы видим ошибку выше (без проблем, если мы выбираем YouTube)
Ниже приведена конфигурация блока контента: [Конфигурация блока контента]
Для рендеринга видео у нас есть скрипт JS. Я включил сценарий JS для Vimeo, так как Youtube, похоже, не является проблемой:
/**
*
* Video Service
*
* Logic to setup different video iframe api services (currently Youtube and Vimeo)
*
* IMPORTANT: IOS devices don't allow you to use the service API to trigger play on the iframe.
* Instead, allow the user to click directly on the ifram, which will work fine.
*
*/
import $ from 'jquery';
import eventBus from './eventBus';
import { loadScriptOnce } from './load';
let playerOrigin = '*';
/**
*
* Helper function for sending a message to the vimeo player, taken from {@link Vimeo API}
* {@link https://developer.vimeo.com/player/js-api Vimeo API}
*
* @param {string} action - Name of the action to be posted to the iframe
* @param {string} value - Value of the action to be posted to the iframe
* @param {HTMLIFrameElement} playerIframe - Iframe DOM element of the player
*
*/
function postToVimeo(action, value, playerIframe) {
var data = {
method: action
};
if(value) {
data.value = value;
}
var message = JSON.stringify(data);
playerIframe.contentWindow.postMessage(message, playerOrigin);
}
/**
*
* Listen for 'message' events from the VIMEO iframe, and call the corresponding
* handler from a provied configuration object
*
* @param {object} config - instance configuration object. Must have valid methods
* Corresponding to the Vimeo API events
* @param {HTMLIframeElement} iframe - Vimeo iframe video player
*
*/
function initVimeo(config, iframe) {
window.addEventListener('message', (e) => {
let data = JSON.parse(e.data);
// Handle messages from the vimeo player only
if (!(/^https?:\/\/player.vimeo.com/).test(e.origin)) {
return false;
}
// Return if the message is not from this iframe
if(iframe.id !== data.player_id) {
return false;
}
if (playerOrigin === '*') {
playerOrigin = e.origin;
}
// Execute the handler for this event, if it's a valid function
if(typeof config[data.event] == 'function') {
config[data.event]()
}
}, false);
}
/**
*
* Youtube calls this function automatically once the API has loaded
*
*/
const youtubeAPIPromise = $.Deferred();
window.onYouTubeIframeAPIReady = () => {
youtubeAPIPromise.resolve();
}
/**
*
* Initialize a video service instance. Returns a `serviceController`
* object for listening to API events and controlling player with custom elements.
*
* @param {string} serviceName - Name of the service to initialize
* @param {HTMLIframeElement} iframe - Iframe video player
* @returns {object} serviceController - service video service controller instance.
*/
function initVideoService(serviceName, iframe) {
let serviceReady = $.Deferred();
// Instace controller
let serviceController = {}
// Since each service has a very different API,
// We have to assemble the `serviceController` for each type
// in a different way, while still providing a consistent interface.
switch (serviceName) {
case 'vimeo':
// Assign the controls methods for vimeo
serviceController.play = () => {
serviceReady.then(() => {
postToVimeo('play', null, iframe);
})
}
serviceController.stop = () => {
serviceReady.then(() => {
postToVimeo('pause', null, iframe);
})
}
initVimeo({
ready() {
// Need to tell Vimeo that we want to listen for this events
postToVimeo('addEventListener', 'play', iframe);
postToVimeo('addEventListener', 'finish', iframe);
serviceReady.resolve();
},
play() {
eventBus.publish('service:play', iframe)
},
finish() {
eventBus.publish('service:finish', iframe)
}
}, iframe)
break;
case 'youtube':
// Assign control methods for youtube
serviceController.play = () => {
serviceReady.then((player) => {
player.playVideo()
})
}
serviceController.stop = () => {
serviceReady.then((player) => {
player.stopVideo()
})
}
loadScriptOnce('https://www.youtube.com/iframe_api');
youtubeAPIPromise.then(() => {
let player = new YT.Player(iframe, {
events: {
onReady() {
serviceReady.resolve(player);
},
onStateChange(e) {
if(e.data == 1) {
eventBus.publish('service:play', iframe)
} else if (e.data == 0) {
eventBus.publish('service:finish', iframe)
}
}
}
})
})
break;
}
return serviceController
}
export default initVideoService
Вот файл ветки для Героя, если он вам нужен:
{% if post.hero_title or post.hero_content %}
<section id="hero" class="hero hero--overlay hero--{{ post.hero_media_type }} js-scroll-section js-image-load">
<div class="hero__body">
<h1 class="hero__title" data-animate="title">{{ post.hero_title }}</h1>
{% if post.hero_content %}
<p data-animate="blurb">{{ post.hero_content }}</p>
{% endif %}
{% if post.hero_add_button %}
{% include 'components/button.twig' with {
addButton: post.get_field( 'hero_add_button' ),
text: post.get_field( 'hero_button_text' ),
type: post.get_field( 'hero_button_type' ),
target: post.get_field( 'hero_button_target_' ~ post.get_field( 'hero_button_type' ) ),
link: post.get_field( 'hero_button_link_' ~ post.get_field( 'hero_button_type' ) ),
attribute: 'data-animate="cta"',
btnAttribute: 'data-toggle-modal="#hero-modal-video"'
} %}
{% if post.hero_button_type == 'video' %}
{% include 'components/modal-video.twig' with {
id: 'hero-modal-video',
video_service: post.hero_video_service,
video_id: post.hero_video_id
} %}
{% endif %}
{% endif %}
{% if post.hero_media_type == 'video' %}
<div id="playpause" tabindex="0">
<button type="button">
<span class="icon-pause" aria-hidden="true"></span>
<span class="text"></span>
</button>
</div>
{% endif %}
</div>
</div>
{% if post.hero_media_type == 'video' %}
<video id="hero-video" class="hero__video js-video-autoplay" autoplay loop>
<source src="{{ post.get_field('hero_video_webm') }}" type="video/webm">
<source src="{{ post.get_field('hero_video_mp4') }}" type="video/mp4">
<source src="{{ post.get_field( 'hero_video_ogv' ) }}" type="video/ogv">
</video>
{% endif %}
{% if post.hero_add_cta %}
<div class="hero__action">
<div class="hero-cta-box hero-cta-box--{{ post.hero_cta_type }}">
{% if post.hero_cta_header %}
<h4 class="hero-cta-box__title {{ post.hero_zip_code_description ? 'mb-05'}}">{{ post.hero_cta_header }}</h4>
{% endif %}
{% if post.hero_cta_type == 'zip_code' %}
{% if post.hero_zip_code_description %}
<p>{{ post.hero_zip_code_description }}</p>
{% endif %}
{% include 'components/zip-code.twig' with {
className: 'form-columns',
labelColor: 'white',
btnText: post.hero_cta_button_text
} %}
{% else %}
{% include 'components/button.twig' with {
addButton: true,
text: post.get_field( 'hero_cta_button_text' ),
type: post.get_field( 'hero_cta_button_type' ),
target: attribute( post, 'button_target_' ~ post.get_field( 'hero_cta_button_type' ) ),
link: attribute( post, 'button_link_' ~ post.get_field( 'hero_cta_button_type' ) ),
wrapperClass: 'hero-cta-box__button'
} %}
{% endif %}
</div>
</div>
{% endif %}
</section><!-- .hero -->
{% endif %}
Пожалуйста, дайте мне знать, будет ли полезна любая другая информация.