Flowplayer · Horizontal scrollable playlist

Video 1 Video 2
Video 3 Video 4
Video 5 Video 6

A horizontal playlist implemented with jQueryTools' scrollable. The videos are offered with adaptive bit rate streaming as MPEG-DASH and HLS streams.

Note: In theory you could make Flowplayer's dedicated playlist element inside the container element scrollable. In practice this breaks fullscreen mode in some browsers. Therefore the playlist user interface is replicated in a dedicated scrollable element and the interaction is scripted by duplicating and adapting some of Flowplayer's playlist code for this purpose.

<head/>

Here is how to load the assets for scrollable.

Caveat: Because jQueryTools are not completely compatible with jQuery version 1.9+ an older version of the jQuery library is used.

<!-- load a pre 1.9 version of the jquery library -->
<script src="//code.jquery.com/jquery-1.8.3.min.js"></script>
<!-- the Flowplayer script as usual -->
<script src="//releases.flowplayer.org/7.0.2/flowplayer.min.js"></script>
<!-- the dashjs plugin for playback of DASH streams - for this demo, otherwise often hlsjs -->
<script src="//releases.flowplayer.org/dashjs/flowplayer.dashjs.min.js"></script>
<!-- jQuery Tools -->
<script src="http://cdn.jquerytools.org/1.2.7/all/jquery.tools.min.js"></script>

HTML

<style>

/* fixed width container */
#player {
width: 800px;
background-color: #036;
}
 
/* scrollable */
div.scrollable-wrap {
display: block;
width: 800px;
height: 60px;
}
.scrollable-wrap .scrollable {
position: relative;
overflow: hidden;
float: left;
width: 730px;
height: 60px;
}
/* playlist items == scrollable items */
.scrollable .items {
width: 999em;
position: absolute;
}
.scrollable .items div {
float: left;
}
.scrollable .items span {
float: left;
padding: 10px;
margin: 10px;
width: 330px;
color: #282828;
background-color: rgba(0, 0, 0, 0.2);
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
background: -webkit-radial-gradient(50% 50%, ellipse closest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0) 100%);
background: -moz-radial-gradient(50% 50%, ellipse closest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0) 100%);
background: -ms-radial-gradient(50% 50%, ellipse closest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0) 100%);
font-size: 16px;
font-weight: bold;
text-align: center;
cursor: pointer;
}
.scrollable .items span.is-active {
background: -webkit-radial-gradient(50% 50%, ellipse closest-corner, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.3) 100%);
background: -moz-radial-gradient(50% 50%, ellipse closest-corner, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.3) 100%);
background: -ms-radial-gradient(50% 50%, ellipse closest-corner, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.3) 100%);
color: #f00;
}
.scrollable .items span.is-paused {
color: #900;
}
/* scrollable prev and next buttons */
.scrollable-wrap a.prev, .scrollable-wrap a.next {
background: url(//jquerytools.github.io/media/img/scrollable/arrow/hori_large.png) no-repeat;
display: block;
float: left;
width: 30px;
height: 30px;
margin: 15px 0;
font-size: 0;
cursor: pointer;
}
.scrollable-wrap a.next {
background-position: 0 -30px;
clear: right;
margin-left: 10px;
}
.scrollable-wrap a.prev:hover {
background-position: -30px 0;
}
.scrollable-wrap a.prev:active {
background-position: -60px 0;
}
.scrollable-wrap a.next:hover {
background-position: -30px -30px;
}
.scrollable-wrap a.next:active {
background-position: -60px -30px;
}

CSS

<script>

$(function () {
var playlist = [],
base = "//edge.flowplayer.org/night",
videos = $(".scrollable span"),
scrollable,
player,
i;
 
// build playlist from videos named night1 thru night6
for (i = 1; i < 7; i += 1) {
playlist.push({
sources: [
{ type: "application/dash+xml", src: base + i + ".mpd" },
{ type: "application/x-mpegurl", src: base + i + ".m3u8" },
{ type: "video/mp4", src: base + i + ".mp4" }
]
});
}
 
$(".scrollable").scrollable({
circular: true
});
 
scrollable = $(".scrollable").data("scrollable");
 
player = flowplayer($("#player"), {
// loop the playlist in a circular scrollable
loop: true,
ratio: 9/16,
splash: true,
bgcolor: "#333333",
customPlaylist: true,
playlist: playlist
 
}).on("ready", function (e, api, video) {
videos.each(function (i) {
var active = i == video.index;
 
$(this).toggleClass("is-active", active)
.toggleClass("is-paused", active && api.paused);
});
 
}).on("pause resume", function (e, api) {
videos.eq(api.video.index).toggleClass("is-paused", e.type == "pause");
 
}).on("finish", function (e, api) {
var vindex = api.video.index,
currentpage = Math.floor(vindex / 2),
scrollindex = scrollable.getIndex(),
next;
 
// advance scrollable every 2nd playlist item
if (vindex % 2 != 0 && scrollindex == currentpage) {
// prefer circular movement when current item is visible
scrollable.next();
 
} else if (scrollindex != currentpage) {
// scroll to next item if not visible
next = vindex < playlist.length - 1 ? vindex + 1 : 0;
scrollable.seekTo(Math.floor(next / 2));
}
});
 
videos.click(function () {
var vindex = videos.index(this);
 
if (player.video.index === vindex) {
player.toggle();
} else {
player.play(vindex);
}
});
});

JavaScript

<body/>

<div id="player"></div>
 
<div class="scrollable-wrap">
<a class="prev">prev</a>
 
<div class="scrollable">
<div class="items">
<div>
<span>Video 1</span> <span>Video 2</span>
</div>
 
<div>
<span>Video 3</span> <span>Video 4</span>
</div>
 
<div>
<span>Video 5</span> <span>Video 6</span>
</div>
</div>
</div>
 
<a class="next">next</a>
</div>

HTML