Sign In

Newsletter

Subscribe to our newsletter to stay updated about Blue Billywig and all advancements regarding online video.

Sign In

Header image for Skinning the Blue Billywig player # 1: Basic controls

Skinning the Blue Billywig player # 1: Basic controls

Posted on October 16, 2015

Tags: css, html, javascript, user interface, video player

In June 2014, we released our new online video player with version five. One of the biggest changes was an all new JavaScript API which enabled every script (thus every developer that uses our video platform) to take full control of the player. In this step-by-step tutorial we will build a new, custom skin using HTML, CSS and JavaScript. The end result can be viewed at the GitHub page).

 

First things first

I was in charge with building the skin used in our player today, and I learned the hard way that building the controls of a video player isn't that easy. There are a number of things that should be accounted for, along with basic functionality like starting, pausing, seeking, and controlling the volume: The controls should work with the mouse and touch, it should scale up on mobile devices, regular controls shouldn't be visible when advertisements are playing and various player sizes have to be accounted for.

In order to keep things simple for this tutorial, we will be focussing on controlling the video player only. I suspect that most front-end developers will have no trouble with supporting mobile devices.

To keep things organised, we will split the tutorial into 3 parts:

  • Part 1: Introduction and basic controls (initial setup and code architecture, play / pause, mute / unmute, time display)
  • Part 2: Advanced controls (progress bar, volume bar, quality selection)
  • Part 3: Start- & endscreen (fullscreen, big play button, title, replay and next video)

An overview of all available events and methods is available on the Blue Billywig Support site: Blue Billywig Player API.

Initial setup

Our code will exist of three things every front-end developer will be familiair with: HTML, CSS and JavaScript. We will create an initial file structure and an index.html that includes the CSS and JavaScript files. We use jQuery to make our life easy, and Font-Awesome for our graphics. Since the example will not be used in a production environment, it will be using CSS techniques that might not be supported on all browsers.

All JavaScript code related to the skin will be written in a single object, please refer to the MDN Introduction to Object-Oriented Javascript for more information on objects in JavaScript. Furthermore, it might be useful to initialise the skin when a player is found on the page. For the sake of simplicity, that won't be covered in this tutorial.

The player object

In order to listen to events from the players and use methods available in the API, we have to get the player object. We will insert a Blue Billywig player with the Launchpad, using an example clip and a playout we created for this tutorial:

player.js

  1. // Create a new object for all player logic.
  2. var myPlayer = {};
  3. // Add an API variable containing the Blue Billywig player
  4. myPlayer.api = new bluebillywig.Player( "http://www.bluebillywig.com/vms.com/p/skin-tutorial/c/2530517.json", {
  5.         target : $('#player')[0]
  6. });

And the HTML that loads the script and contains a div element in which the player will be rendered:

index.html

  1. <script src="http://www.bluebillywig.com/vms.com/launchpad/"></script>
  2. <div id="player">
  3. </div>

In our playout options, we disable every visual element we can find: Hide the controlbar, the big play button, title and related items. The only thing that will be visible is the video itself. The size is also set to 100% width and height, since we want our skin to decide on the player size. For now, we will use a static 1280x720:

player.css

  1. #player {
  2.         position: relative;
  3.         height: 720px;
  4.         width: 1280px;
  5.         margin: 20px auto;
  6. }

The skin container

Since we want our skin to appear on top of the video, we create a new element (#skin) inside the #player container which will contain all skin elements like the controlbar, big play button, title and more.

index.html

  1. <div id="player">
  2.         <div class="skin">
  3.                 <div class="controlbar"></div>
  4.         </div>
  5. </div>

player.css

  1. .skin {
  2.         /* Overlay skin on top of the video */
  3.         position: absolute;
  4.         top: 0;
  5.         bottom: 0;
  6.         left: 0;
  7.         right: 0;
  8.         z-index: 1;
  9. }
  10. .controlbar {
  11.         /* Stick controlbar to the bottom of the skin */
  12.         position: absolute;
  13.         bottom: 0;
  14.         left: 0;
  15.         right: 0;
  16. }

Adding a play and pause button

Now for the fun part! Let's add some buttons to our skin:

index.html

  1. <div class="controlbar">
  2.         <div class="button play">
  3.                 <i class="fa fa-play"></i>
  4.         </div>
  5.         <div class="button pause">
  6.                 <i class="fa fa-pause"></i>
  7.         </div>
  8. </div>

"Woah, two separate buttons for playing and pausing?"

Yes! We will be using a class on our #player element that specifies wether the video is paused or playing, allowing us to use CSS to display the play button, or the pause button.

player.css

  1. /* Show play button on default */
  2. .pause.button {
  3.         display: none;
  4. }
  5. .play.button {
  6.         display: block;
  7. }
  8. #player.playing .play.button {
  9.         display: none;
  10. }
  11. #player.playing .pause.button {
  12.         display: block;
  13. }

Using methods to control the player

In order for the buttons to actually do something, we have to bind a function to it that plays or pauses the video. We create two functions that issue the play or pause method from the player API. We could use the player API directly, but using a function is a good practice:

player.js

  1. myPlayer.play = function(){
  2.         this.api.play();
  3. };
  4. myPlayer.pause = function(){
  5.         this.api.pause();
  6. };

Next, we need to bind the buttons to these functions. We create an init() function that will take care of all these bindings and other logic that has to be executed once.

player.js

  1. myPlayer.init = function(targetContainer){
  2.         this.$container = $(targetContainer);
  3.         // Find the skin container
  4.         this.$skin = this.$container.find('.skin');
  5.         // Find the controlbar
  6.         this.$controlbar = this.$skin.find('.controlbar');
  7.         // Bind elements
  8.         this.$controlbar.find('.play.button').on('click', $.proxy(this.play, this));
  9.         this.$controlbar.find('.pause.button').on('click', $.proxy(this.pause, this));
  10. };

In order to execute this function correctly, we have to bind it to the ready event from the API. Since we cannot bind directly on the player at this moment (since it does not yet exist), we bind it to the parent element to which the event bubbles. It is important that we bind this function beforewe load the player:

player.js

  1. // Listen to ready event that bubbles from the player
  2. $('#player').on('ready', function(){
  3.         myPlayer.init(this);
  4. });
  5. // Add an API variable containing the Blue Billywig player
  6. myPlayer.api = new bluebillywig.Player( "http://www.bluebillywig.com/vms.com/p/skin-tutorial/c/2530517.json", {
  7.         target : $('#player')[0]
  8. });

Right now, we should be able to start the player using the start button! But still one thing is missing: The play button does not change into a pause button. This is where we will experience the convenience of the CSS styling that switches the buttons.

Listening to events

We will create two functions that handle the playing and paused events. We use theplaying event instead of play, since the playing event will be thrown when the video actually plays. If, for some reason, the video failed to play, it will correctly be reflected in our skin.

In our init function, we will add the event handlers that are bound to the API:

player.js

  1. // Bind api events
  2. this.api.on('playing', $.proxy(this.onPlaying, this));
  3. this.api.on('pause', $.proxy(this.onPause, this));
  4. this.playing = false;

And we create two functions that are executed:

player.js

  1. myPlayer.onPlaying = function(){
  2.         // Logically, the video isn't paused when it's playing
  3.         this.$container.removeClass('paused').addClass('playing');
  4. };
  5. myPlayer.onPause = function(){
  6.         // Logically, the video isn't playing when it's paused
  7.         this.$container.removeClass('playing').addClass('paused');
  8. };

You will notice that the player also throws a pause event at the end of the video, and that issuing a play command starts playing the video from the start. For now, this is the intended behaviour, but we could change it later on using the ended event.

We basically build our skin around the player, hooking into the players' events to change things, instead of issuing commands directly in the skin. This is very good practice, since our skin will reflect exactly what is happening in the player.

The mute button

The mute button is in many ways very similar to the play / pause button, which means we can use the same workflow:

index.html

  1. <div class="mute button">
  2.         <i class="fa fa-volume-up"></i>
  3. </div>
  4. <div class="unmute button">
  5.         <i class="fa fa-volume-off"></i>
  6. </div>

player.css

  1. /* Show mute button on default */
  2. .pause.unmute {
  3.         display: none;
  4. }
  5. .play.mute {
  6.         display: block;
  7. }
  8. #player.muted .mute.button {
  9.         display: none;
  10. }
  11. #player.muted .unmute.button {
  12.         display: block;
  13. }

player.js

  1. myPlayer.mute = function(){
  2.         this.api.setMuted(true);
  3. };
  4. myPlayer.unmute = function(){
  5.         this.api.setMuted(false);
  6. };

In the init function:

player.js

  1. this.$controlbar.find('.mute.button').on('click', $.proxy(this.mute, this));
  2. this.$controlbar.find('.unmute.button').on('click', $.proxy(this.unmute, this));

"But wait, there are not muted and unmuted events!"

This is correct. Instead, there is a volumechange event which indicates some form of change in volume (including mute / unmute). The reason behind this, is the fact that the volume not only can be changed by the mute button, but also by (for example) a volume slider.

player.js

  1. this.api.on('volumechange', $.proxy(this.onVolumeChange, this));

The result is that our onVolumeChange function gets a little bit more interesting. For now, we only have to deal with the muted status:

player.js

  1. myPlayer.onVolumeChange = function(){
  2.         if(this.api.getMuted() == true){
  3.                 this.$container.addClass('muted');
  4.         } else {
  5.                 this.$container.removeClass('muted');
  6.         }
  7. };

For every volume change, we get the muted status from the API and add or remove the classes. If we extend our skin with a volume control later on, we will extend this function.

Time display

Not everything in our skin is a button, some things just display something. Lets take a loot at displaying the time in the controlbar. We create a time container with appropriate elements to write a time in, and create a variable in our init function for easier access.

index.html

  1. <div class="time">
  2.         <span class="elapsed">00:00</span>/<span class="duration">00:00</span>
  3. </div>

player.js

  1. this.$time = this.$controlbar.find('.time');

First, the duration (or total time) of the video. We can use the getDuration method in the api to get the duration of the video. We do this when the durationchange event has been thrown:

player.js

  1. this.api.on('durationchange', $.proxy(this.onDurationChange, this));

In our onDurationChange event, we get the duration and print it into the desired element. Since the duration is returned in seconds, we need a function to convert it to something that is formatted. We can insert the formatted string into the element.

player.js

  1. myPlayer.onDurationChange = function(){
  2.         var duration = this.api.getDuration();
  3.         this.$time.find('.duration').html(this.formatSeconds(duration));
  4. };

The same procedure is followed with the elapsed time, using the timeupdate event:

player.js

  1. this.api.on('timeupdate', $.proxy(this.onTimeUpdate, this));

Note that the timeupdate event is throttled due to performance reasons. This doesn't matter for now, since we only display seconds.

player.js

  1. myPlayer.onTimeUpdate = function(){
  2.         var currentTime = this.api.getCurrentTime();
  3.         this.$time.find('.elapsed').html(this.formatSeconds(currentTime));
  4. };

The way the time is formatted is fully customisable, and allows you to use elapsed time, remaining time, or even percentages. Do remember to account for videos that are more than an hour long. Even if you have none of those right now, they might be used in the future!

Wrapping up

That's it! We created a very simple skin with the Blue Billywig Player API, with a play / pause button, a mute / unmute button and a time display. There are many more features that can be implemented, but those will be covered part 2 and part 3 in the future. If you want to be kept informed, please subscribe to our newsletter at the top of this page!

The end result is available at the GitHub page. To view the full code, please visit the Github page with the source code.

Author Blog author avatar

Blue Billywig


We use cookies to provide social media features and to analyse our traffic. We also share information about your use of our site with our social media and analytics partners who may combine it with other information that you’ve provided to them or that they’ve collected from your use of their services. You consent to our cookies if you continue to use our website. Read moreOK