10

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)

akn
  • 3,712
  • 26
  • 43
  • 1
    This seems to be the same issue - http://stackoverflow.com/questions/28180672/youtube-cannot-swipe-past-iframe-in-carousel-slider - where they recommend using a image in place of the iframe at the start. Is this a viable solution? – jjbskir Jan 14 '16 at 20:09
  • @jjbskir, hmm, I could use image with custom play button, and replace the video and the image on click with ng-show, but if user pause the video he won't be able to swipe again. Also I don't know if it is possible to start the video with custom button ;) – akn Jan 14 '16 at 20:21

2 Answers2

2

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>
fnune
  • 5,256
  • 1
  • 21
  • 35
2

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';
    }
}
jjbskir
  • 8,474
  • 9
  • 40
  • 53