HTML5 Video player jQuery plugin
As you know – HTML5 <video> element is already supported by most of browsers (by modern browsers). Its initialization is very easy. During today’s investigation I have understood few things: that each browser supports only few of video formats, and each browser has own native video controls (and all of them are different). But fortunately, html5 can give us all necessary possibilities to make own interface to control our video element. Today I will show you process of building own html5 player (quite crossbrowser), more, it will be new jquery plugin.
Final result of our player:
Here are our demo and downloadable package:
Live Demo
download in package
Ok, download the source files and lets start coding !
Step 1. HTML Markup
This is markup of our result slideshow page. Here it is:
index.html
<div class="video_player"> <video controls="controls" poster="media/poster.jpg" style="width:800px;"> <source src="media/video.ogg" type="video/ogg" /> <source src="media/video.mp4" type="video/mp4" /> <source src="media/video.webm" type="video/webm" /> </video> <div class="custom_controls"> <a class="play" title="Play"></a> <a class="pause" title="Pause"></a> <div class="time_slider"></div> <div class="timer">00:00</div> <div class="volume"> <div class="volume_slider"></div> <a class="mute" title="Mute"></a> <a class="unmute" title="Unmute"></a> </div> </div> </div> <script> $(function() { $('.video_player').myPlayer(); }); </script>
You can see here and our Video element, and the custom controls set. Plus – jquery plugin initialization. As you can see – I have to provide 3 file formats (ogg, mp4 and webm) to cover all browsers (FF, IE, Chrome, Safari and possible Opera too). As video converter software, I can recommend Miro Video Converter (http://www.mirovideoconverter.com/) as example.
Step 2. CSS
Here are all stylesheets
css/player.css
/* jquery */ .ui-slider-handle { display: block; margin-left: -9px; position: absolute; z-index: 2; } .ui-slider-range { bottom: 0; display: block; height: 100%; left: 0; position: absolute; width: 100%; z-index: 1; } /* player */ .video_player { background-color: #E8E8E8; border: 1px solid #888; float: left; padding: 10px; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; } /* controls */ .video_player .custom_controls { clear: both; height: 30px; padding-top: 8px; position: relative; width: 100%; } .play, .pause, .volume, .time_slider, .timer { float: left; } .play, .pause, .mute, .unmute { cursor: pointer; } .play, .pause { display: block; height: 24px; margin-left: 5px; margin-right: 15px; opacity: 0.8; width: 33px; transition: all 0.2s ease-in-out; -moz-transition: all 0.2s ease-in-out; -webkit-transition: all 0.2s ease-in-out; -o-transition: all 0.2s ease-in-out; } .play { background: url(../images/play.png) no-repeat; } .pause { background: url(../images/pause.png) no-repeat; display: none; } .play:hover, .pause:hover { opacity: 1; } .time_slider { border: 1px solid #5f5f5f; height: 10px; margin-top: 5px; position: relative; width: 630px; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; background: #777777; background-image: -moz-linear-gradient(top, #777777, #9d9d9d); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #777777),color-stop(1, #9d9d9d)); } .time_slider .ui-slider-handle { background: url(../images/handler.png) no-repeat; cursor: pointer; height: 16px; opacity: 0.8; top: -2px; width: 16px; } .time_slider .ui-slider-handle.ui-state-hover { opacity: 1; } .time_slider .ui-slider-range { border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; background: #e9742e; background-image: -moz-linear-gradient(top, #e9742e, #c14901); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #e9742e),color-stop(1, #c14901)); } .timer { color: #000; font-size: 12px; margin-left: 10px; margin-top: 3px; } .volume { bottom: 0; color: #FFFFFF; height: 35px; overflow: hidden; padding: 5px 10px 0; position: absolute; right: 0; width: 33px; } .volume:hover { background: url(../images/volume.png) no-repeat scroll 8px 0 transparent; height: 161px; } .volume_slider { height: 105px; left: -1px; opacity: 0; position: relative; width: 33px; } .volume:hover .volume_slider { opacity: 1; } .volume_slider .ui-slider-handle { background: url(../images/handler.png) no-repeat; height: 15px; left: 9px; margin-bottom: -15px; margin-left: 0; opacity: 0.8; width: 14px; } .volume_slider .ui-slider-handle.ui-state-hover { opacity: 1; } .mute, .unmute { bottom: 7px; display: block; height: 23px; opacity: 0.8; position: absolute; text-indent: -999px; width: 33px; } .mute:hover, .unmute:hover { opacity: 1; } .mute { background: url(../images/vol_full.png) no-repeat; } .unmute { background: url(../images/vol_mute.png) no-repeat; display: none; }
Step 3. JS
js/script.js
function rectime(secs) { var hr = Math.floor(secs / 3600); var min = Math.floor((secs - (hr * 3600))/60); var sec = Math.floor(secs - (hr * 3600) - (min * 60)); if (hr < 10) {hr = '0' + hr; } if (min < 10) {min = '0' + min;} if (sec < 10) {sec = '0' + sec;} if (hr) {hr = '00';} return hr + ':' + min + ':' + sec; } (function($) { $.fn.myPlayer = function() { return this.each(function() { // variables var $oMain = $(this); var $oVideo = $('video', $oMain); var $oPlay = $('.play', $oMain); var $oPause = $('.pause', $oMain); var $oTimeSlider = $('.time_slider', $oMain); var $oTimer = $('.timer', $oMain); var $oVolSlider = $('.volume_slider', $oMain); var $oMute = $('.mute', $oMain); var $oUnmute = $('.unmute', $oMain); var bTimeSlide = false; var iVolume = 1; // functions section var prepareTimeSlider = function() { if (! $oVideo[0].readyState) { setTimeout(prepareTimeSlider, 1000); } else { $oTimeSlider.slider({ value: 0, step: 0.01, orientation: 'horizontal', range: 'min', max: $oVideo[0].duration, animate: true, slide: function() { bTimeSlide = true; }, stop:function(e, ui) { bTimeSlide = false; $oVideo[0].currentTime = ui.value; } }); }; }; // events section $oPlay.click(function () { $oVideo[0].play(); $oPlay.hide(); $oPause.css('display', 'block'); }); $oPause.click(function () { $oVideo[0].pause(); $oPause.hide(); $oPlay.css('display', 'block'); }); $oMute.click(function () { $oVideo[0].muted = true; $oVolSlider.slider('value', '0'); $oMute.hide(); $oUnmute.css('display', 'block'); }); $oUnmute.click(function () { $oVideo[0].muted = false; $oVolSlider.slider('value', iVolume); $oUnmute.hide(); $oMute.css('display', 'block'); }); // bind extra inner events $oVideo.bind('ended', function() { $oVideo[0].pause(); $oPause.hide(); $oPlay.css('display', 'block'); }); $oVideo.bind('timeupdate', function() { var iNow = $oVideo[0].currentTime; $oTimer.text(rectime(iNow)); if (! bTimeSlide) $oTimeSlider.slider('value', iNow); }); // rest initialization $oVolSlider.slider({ value: 1, orientation: 'vertical', range: 'min', max: 1, step: 0.02, animate: true, slide: function(e, ui) { $oVideo[0].muted = false; iVolume = ui.value; $oVideo[0].volume = ui.value; } }); prepareTimeSlider(); $oVideo.removeAttr('controls'); }); }; })(jQuery);
As you see – this is simple jQuery plugin. In the beginning – I have prepared all necessary controls and variables. Then – I have binded ‘onclick’ events to our controls, plus several inner events of video player (like ‘ended’ and ‘timeupdate’). After, I have implemented two jQueryUI sliders (time slider and volume slider). At the end – I have removed default controls: $oVideo.removeAttr(‘controls’); Generally – thats all.
Step 4. Images
For our html5 video player I have used next images:
Live Demo
download in package
Conclusion
Hope that today’s html5 tutorial was great. We have made another one nice html5 example. I will be glad to see your thanks and comments. Good luck!
Wow, the game has changed with the announcement that Adobe is no longer supporting development for FLASH for mobile devices or TV…it is focusing on youtube html5 and Adobe AIR apps instead….the news got around the game development community quickly. I think the market penetration of mobile devices like the iPad, iPhone, and iTouch from Apple being a merket leader who doesn’t support FLASH made a true impression on developers whose clients pages were losing views by this audience. Comsider Apple’s leadership history introducing CD ROM’s fiirst in computers, dropping the beige look of computers, introucing the iMAc, iTunes, Quicktime, and Jobs support of Blu Ray at Disney helped set standards in many industries…
Hey! This is once again a great tutorial as usual.
Thanks a lot Andrew for your outstanding job here.
But this time, I do have some issue with the video controls and the global aspect in Chome browser, here are the details:
First, the custom controler with the nice buttons does not seem to work in any browser i have tested (Safari 5.1.7, Chrome 19 and Firefox 13.0). I have noticed that you’ve had in the package html file this notation:
google.load(“jquery”, “1.7.1″);
google.load(“jqueryui”, “1.8.16″);
This, if i understand, should be the google/jQuery default controls (so these controls works fine anywhere). Do they actually take over the custom controllers?
Second, i’m a bit confuse with this statement in the scrip.js file “At the end – I have removed default controls: $oVideo.removeAttr(‘controls’);” since in my case, the custom and the default controls showed in every browser. Is my problem with the controls is related with this line of code?
Third, the layout is quite different in Chrome as the height of the video window is about twice taller than it should be. I have probably screwed up here, can you tell me where in the code i have missed or what should i add to it in order to fix Chrome behavior? I have tried naively to fix the height in the CSS but it just hided the bottom…
You can take a look at the web page (I’ve have code commented the custom controls to hide them) http://dominicbrown.ca/dbVideos.html
You’ll also see one of your template slightly transformed and integrated all over my web site (Thanks again, links are still there as you can see).
Hope you will be able to help me with this!
Hello Dominic my friend,
As the first –
google.load(“jquery”, “1.7.1″);
google.load(“jqueryui”, “1.8.16″);
this is not custom google controls. This is free google’s service to obtain jQuery libraries (of different version). So, this script doesn’t link custom controls at all, it loads jQuery library version 1.7.1 and jQuery UI version 1.8.16.
I can recommend you to check your image: VanHoutte_still.jpg
or, you can also define height of video player too. Please pay attention to our Video element. It has only Width value, you can try to add Height as well.
My regards.
how to include a ‘fullscreen’ buttom?
There is the one of params:
fullScreen
Boolean : (Default: false) : Sets the initial state of the full screen mode.