I'm trying to build simply slider with images and youtube videos. I want make it works fine on touch devices, so I want to use ng-swipe-*
from angular's ngTouch
module. Unfortunately swipe doesn't work over youtube's iframe. I tried to set lower z-index: -10;
, but then I cannot play the video.
Do you have any idea how to solve this problem?
There is a snippet:
var app = angular.module('app', ['ngTouch']);
app.controller('ctrl', function($scope) {
$scope.msg = function(msg) {
alert(msg);
}
});
.ok {
width: 300px;
height: 100px;
background: green;
}
<script src="https://code.angularjs.org/1.4.8/angular.min.js"></script>
<script src="https://code.angularjs.org/1.4.8/angular-touch.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl" ng-swipe-right="msg('right')" ng-swipe-left="msg('left')">
<div class="ok">swipe works here</div>
<div>
<iframe width="300" height="200" src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allowfullscreen></iframe>
</div>
</div>
</div>
(the best way to test it, is to run it in Chrome developer console and emulate on touch device)
Here's a pretty hacky workaround: using two overlay divs, set to the right and the left of the player allows the user to play and pause, and setting the height to 80% allows them to use the menu on the bottom. This is not perfect, but it kind of works!
Note 1: It's kind of buggy if you play it here, so I'm adding a codepen: http://codepen.io/anon/pen/LGjwYZ
Second version, a little bit more bloated but with more area coverage: http://codepen.io/anon/pen/rxzXxB
Note 2: I used a transparent background on the divs for demonstrational purposes.
var app = angular.module('app', ['ngTouch']);
app.controller('ctrl', function($scope) {
$scope.msg = function(msg) {
alert(msg);
}
});
.ok {
width: 300px;
height: 100px;
background: green;
}
<script src="https://code.angularjs.org/1.4.8/angular.min.js"></script>
<script src="https://code.angularjs.org/1.4.8/angular-touch.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl" ng-swipe-right="msg('right')" ng-swipe-left="msg('left')">
<div class="ok">swipe works here</div>
<div style="position:relative; height:200px; width:300px;">
<iframe style="position:absolute;width:100%;height:100%;z-index:10;" src="https://www.youtube.com/embed/dQw4w9WgXcQ"></iframe>
<div style="background:rgba(0,0,0,0.3);height:80%;width:40%;left:0;position:absolute;z-index:20;"></div>
<div style="background:rgba(0,0,0,0.3);height:80%;width:40%;right:0;position:absolute;z-index:20;"></div>
</div>
</div>
</div>
The issue is that you don't have control of events within the iframe, so can't tell when the user swipes over that area. The work around I suggest is to replace the iframe with a image placeholder while not watching. In order to do this use YouTube's Iframe API to keep track of video events. When the video goes from play to pause we will hide the video and show the image. Here is a demo.
HTML
<div ng-app="app">
<div ng-controller="ctrl" ng-swipe-right="msg($event, 'right')" ng-swipe-left="msg($event, 'left')">
<div id="player"></div>
<img id="player-cover" src="http://img.youtube.com/vi/M7lc1UVf-VE/hqdefault.jpg" />
</div>
</div>
JS
On initiation it hides the video. When ever the image is clicked it shows and plays the video. When the video goes from paused to play onPlayerStateChange
handles toggling the image and video. Every time the swiping event gets called on the image it also triggers the click event handler for the image. The variable swiping
keeps track of if the event was just a click or also a swipe.
var app = angular.module('app', ['ngTouch']);
app.controller('ctrl', function($scope) {
$scope.msg = function(event, msg) {
swiping = true;
alert(msg);
}
});
// keep track of the user swiping. onYouTubeIframeAPIReady needs to occur outside of Angular.
var swiping = false;
// Youtube related.
document.getElementById('player').style.display = 'none';
document.getElementById('player-cover').addEventListener("click", function() {
if (swiping) {
swiping = false;
return;
}
document.getElementById('player').style.display = 'block';
player.playVideo();
});
// This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// This function creates an <iframe> (and YouTube player) after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: { 'onStateChange': onPlayerStateChange }
});
}
function onPlayerStateChange(event) {
if (event.data === YT.PlayerState.PAUSED || event.data === YT.PlayerState.ENDED) {
document.getElementById('player-cover').style.display = 'block';
document.getElementById('player').style.display = 'none';
} else {
document.getElementById('player-cover').style.display = 'none';
document.getElementById('player').display = 'block';
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With