关于HTML 5中Video接口的详细介绍可以参看W3School的HTML 5 视频/音频参考手册,这是写一个播放器的基础。闲言少叙,直切正题,下面是播放器的最终效果

首先来看一下HTML代码以及播放器的调用:

<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>VideoPlayer</title>
	<link rel="stylesheet" type="text/css" href="assets/c/VideoPlayer.css">
	<script type="text/javascript" src="assets/j/jquery-1.8.3.min.js"></script>
	<script type="text/javascript" src="assets/j/VideoPlayer.js"></script>
	<script type="text/javascript">
		$(function(){
			var vp = new VideoPlayer($('.videoWrap'), 'http://yourVideoFile.mp4',578,326, false);
		});
	</script>
</head>
<body>
	<div class="videoWrap">

	</div>
</body>
</html>

 

接下来是JavaScript代码:

/*
 * Author: David;
 * Website: www.CodingSerf.com;
 * Timestamp: 2013-08-29;
*/
var VideoPlayer = function($customerWrap, src, width, height, autoplay){
	this.$customerWrap = $($customerWrap);
	this.autoplay = !!autoplay;
	this.src = src || '';
	this.width = width || 768;
	this.height = height || 432;

	//init VideoPlayer Methods
	this.initVideoPlayer();
};

VideoPlayer.prototype = {
	initVideoPlayer : function(){
		this.$media = $('<video>Oops! Your browser does not support the video tag</video>').addClass('videoPlayerMedia');
		this.media = this.$media[0];
		this.media.width = this.width;
		this.media.height = this.height;
		this.media.controls = false;
		this.media.autoplay = this.autoplay;
		this.media.preload = 'auto';
		this.$vpWrap = $('<div/>').addClass('videoPlayerWrap').css({'width':this.width,'height':this.height}).append(this.$media).appendTo(this.$customerWrap);

		var _self = this;
		this.$media.on('loadedmetadata', function(event){
			_self.duration = event.currentTarget.duration;
		});
		this.initControls();
		//开始加载
		this.media.src = this.src;
	},
	initControls : function(){
		var controlsHTML =
		'<div class="videoPlayerControls">'
			+'<div class="videoPlayerControls-btn _videoPlayerControls-playBtn">'
				+'<i class="videoPlayerControls-btn-icon"></i>'
			+'</div>'
			+'<div class="videoPlayerControls-timeline">'
				+'<div class="videoPlayerControls-progressWrap _videoPlayerControls-timelineProgressWrap">'
					+'<div class="videoPlayerControls-progress _videoPlayerControls-timelinePlayProgress"></div>'
					+'<div class="videoPlayerControls-progress _videoPlayerControls-timelineBufferProgress"></div>'
				+'</div>'
			+'</div>'
			+'<div class="videoPlayerControls-btn _videoPlayerControls-audioBtn">'
				+'<i class="videoPlayerControls-btn-icon videoPlayerControls-btn-icon_audioLoud"></i>'
				+'<div class="videoPlayerControls-audioVolume">'
					+'<div class="videoPlayerControls-progressWrap _videoPlayerControls-volumeProgressWrap">'
						+'<div class="videoPlayerControls-progress _videoPlayerControls-volumeProgress"></div>'
					+'</div>'
				+'</div>'
			+'</div>'
			+'<div class="videoPlayerControls-btn _videoPlayerControls-fullscreenBtn">'
				+'<i class="videoPlayerControls-btn-icon videoPlayerControls-btn-icon_screenExpand"></i>'
			+'</div>'
		+'</div>';
		this.$controls = $(controlsHTML);
		this.$vpWrap.append(this.$controls);
		this.initPlayBtn();
		this.initTimeline();
		this.initAudioBtn();
		this.initFullscreenBtn();
	},
	initPlayBtn : function(){
		this.$playBtn = this.$controls.find('._videoPlayerControls-playBtn');
		this.$playBtnIcon = this.$playBtn.find('.videoPlayerControls-btn-icon');
		this.autoplay ? this.$playBtnIcon.addClass('videoPlayerControls-btn-icon_videoPause') : this.$playBtnIcon.addClass('videoPlayerControls-btn-icon_videoPlay');
		var _self = this;
		this.$playBtn.on('click', function(event){
			if(_self.media.src === '') return;
			_self.$playBtnIcon.attr('class','videoPlayerControls-btn-icon');
			if(_self.media.paused){
				_self.media.play();
				_self.$playBtnIcon.addClass('videoPlayerControls-btn-icon_videoPause');
			}else{
				_self.media.pause();
				_self.$playBtnIcon.addClass('videoPlayerControls-btn-icon_videoPlay');
			}
		});
		this.$media.on('ended', function(event){
			_self.$playBtnIcon.attr('class','videoPlayerControls-btn-icon');
			_self.$playBtnIcon.addClass('videoPlayerControls-btn-icon_videoPlay');
		});
	},
	initTimeline : function(){
		this.$timeline = this.$controls.find('.videoPlayerControls-timeline');
		this.$timelineProgressWrap = this.$timeline.find('._videoPlayerControls-timelineProgressWrap');
		this.$timelinePlayProgress = this.$timelineProgressWrap.find('._videoPlayerControls-timelinePlayProgress');
		this.$timelineBufferProgress = this.$timelineProgressWrap.find('._videoPlayerControls-timelineBufferProgress');
		this.updateProgress(this.$timelinePlayProgress, '0');
		this.updateProgress(this.$timelineBufferProgress, '0');
		var _self = this;
		this.$media.on('timeupdate', function(event){
			var curPercent = _self.media.currentTime / _self.duration;
			_self.updateProgress(_self.$timelinePlayProgress, curPercent);
		});
		this.$media.on('progress',function(event){
			if(!_self.media.buffered.length) return;
			var curPercent = _self.media.buffered.end(_self.media.buffered.length-1) / _self.duration;
			_self.updateProgress(_self.$timelineBufferProgress, curPercent);
		});
		this.$timelineProgressWrap.on('mousedown',function(event){
			var curPercent = (event.pageX-_self.$timelineProgressWrap.offset().left) / event.currentTarget.offsetWidth;
			_self.media.currentTime = curPercent*_self.duration;
			_self.$timelineProgressWrap.on('mousemove', function(evt){
				var movePercent = (evt.pageX-_self.$timelineProgressWrap.offset().left) / evt.currentTarget.offsetWidth;
				movePercent>1 && (movePercent = 1);
				movePercent<0 && (movePercent = 0);
				_self.media.currentTime = movePercent*_self.duration;
			});
			$('body').one('mouseup', function(){
				_self.$timelineProgressWrap.off('mousemove');
			});
		});
	},
	updateProgress : function($progressBar, percent, isVertical){
		!!isVertical ? $progressBar.css({'height':percent*100+'%'}) : $progressBar.css({'width':percent*100+'%'});
	},
	initAudioBtn : function(){
		this.$audioBtn = this.$controls.find('._videoPlayerControls-audioBtn');
		this.$audioBtnIcon = this.$audioBtn.find('.videoPlayerControls-btn-icon');
		this.$audioVolume = this.$audioBtn.find('.videoPlayerControls-audioVolume');
		this.$volumeProgressWrap = this.$audioBtn.find('._videoPlayerControls-volumeProgressWrap');
		this.$volumeProgress = this.$volumeProgressWrap.find('._videoPlayerControls-volumeProgress');
		var _self = this;
		this.$audioBtn.on('click',function(event){
			if(_self.media.volume){
				_self.beforeMuteVolume = _self.media.volume;
				_self.media.volume = 0;
			}else{
				_self.media.volume = _self.beforeMuteVolume;
			}
		});
		this.$audioVolume.on('click',function(event){
			event.stopImmediatePropagation();
		});
		this.$media.on('volumechange', function(event){
			var volume = _self.media.volume;
			_self.$audioBtnIcon.attr('class','videoPlayerControls-btn-icon');
			if(volume == 0.0){
				_self.$audioBtnIcon.addClass('videoPlayerControls-btn-icon_audioMute');
			}
			if(volume>0.0 && volume<0.5){
				_self.$audioBtnIcon.addClass('videoPlayerControls-btn-icon_audioMedium');
			}
			if(volume>=0.5 && volume<=1.0){
				_self.$audioBtnIcon.addClass('videoPlayerControls-btn-icon_audioLoud');
			}
			_self.updateProgress(_self.$volumeProgress, volume, true);
		});
		this.$volumeProgressWrap.on('mousedown',function(event){
			var curPercent = (event.currentTarget.offsetHeight-(event.pageY-_self.$volumeProgressWrap.offset().top)) / event.currentTarget.offsetHeight;
			_self.media.volume = curPercent;
			_self.$volumeProgressWrap.on('mousemove', function(evt){
				var movePercent = (evt.currentTarget.offsetHeight-(evt.pageY-_self.$volumeProgressWrap.offset().top)) / evt.currentTarget.offsetHeight;
				movePercent>1 && (movePercent = 1.0);
				movePercent<0 && (movePercent = 0.0);
				_self.media.volume = movePercent;
			});
			$('body').one('mouseup', function(){
				_self.$volumeProgressWrap.off('mousemove');
			});
		});

		this.media.volume = 0.49;
		this.updateProgress(this.$volumeProgress, this.media.volume, true);
		this.beforeMuteVolume = this.media.volume;
	},
	initFullscreenBtn : function(){
		this.$fullscreenBtn = this.$controls.find('._videoPlayerControls-fullscreenBtn');
		this.$fullscreenBtnIcon = this.$fullscreenBtn.find('.videoPlayerControls-btn-icon');
		var _self = this;
		this.$fullscreenBtn.on('click',function(event){
			if($.isFunction(_self.media.webkitEnterFullscreen)) {
				_self.media.webkitEnterFullscreen();
			}
			else if ($.isFunction(_self.media.mozRequestFullScreen)) {
				_self.media.mozRequestFullScreen();
			}
			else {
				alert('Your browser doesn\'t support fullscreen');
			}

		});
	},
	mediaPlay : function(){
		this.media.paused && this.$playBtn.trigger('click');
	},
	mediaPause : function(){
		this.media.paused || this.$playBtn.trigger('click');
	}
};

 

最后是CSS代码:

@charset "utf-8";
/* CSS Document
 * Author: David;
 * Website: www.CodingSerf.com;
 * Timestamp: 2013-08-29;
*/
.videoPlayerWrap{
	position: relative;
	overflow: hidden;
	background-color: #000;
}
.videoPlayerMedia{
	color: #fff;
}
.videoPlayer{
	margin: 0;
	padding: 0;
	border: none;
}
.videoPlayerWrap:hover .videoPlayerControls{
	bottom: 0;
}
.videoPlayerControls{
	display: -webkit-box;
	display: -moz-box;
	display: -ms-flexbox;
	display: -webkit-flex;
	display: box;
	display: flex;
	width: 100%;
	height: 50px;
	position: absolute;
	left: 0;
	z-index: 200;
	bottom: -50px;
	background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(0.2, rgba(255,255,255,.5)), to(rgba(0,0,0,.5)));
	background-image: -webkit-linear-gradient(rgba(255,255,255,.5) 20%, rgba(0,0,0,.5));
	background-image: -moz-linear-gradient(rgba(255,255,255,.5) 20%, rgba(0,0,0,.5));
	background-image: -o-linear-gradient(rgba(255,255,255,.5) 20%, rgba(0,0,0,.5));
	background-image: linear-gradient(rgba(255,255,255,.5) 20%, rgba(0,0,0,.5));
	-webkit-transition: bottom .3s ease-out;
	-moz-transition: bottom .3s ease-out;
	-ms-transition: bottom .3s ease-out;
	-o-transition: bottom .3s ease-out;
	transition: bottom .3s ease-out;
}
.videoPlayerControls-btn{
	width: 50px;
	height: 50px;
	float: left;
	position: relative;
}
.videoPlayerControls-btn:hover{
	-webkit-box-shadow: 0 0 20px rgba(55,55,55,.8) inset;
	-moz-box-shadow: 0 0 20px rgba(55,55,55,.8) inset;
	box-shadow: 0 0 20px rgba(55,55,55,.8) inset;
}
.videoPlayerControls-btn:hover .videoPlayerControls-btn-icon{
	opacity: .8;
}
.videoPlayerControls-btn-icon{
	display: block;
	width: 24px;
	height: 24px;
	position: absolute;
	top: 50%;
	left: 50%;
	margin: -12px 0 0 -12px;
	background: url(i/icons.png) no-repeat;
	opacity: .6;
}
.videoPlayerControls-btn-icon_videoPlay{
	background-position: 0 0;
}
.videoPlayerControls-btn-icon_videoPause{
	background-position: 0 -24px;
}
.videoPlayerControls-btn-icon_audioLow{
	background-position: -24px 0;
}
.videoPlayerControls-btn-icon_audioMedium{
	background-position: -24px -24px;
}
.videoPlayerControls-btn-icon_audioLoud{
	background-position: -24px -48px;
}
.videoPlayerControls-btn-icon_audioMute{
	background-position: -24px -72px;
}
.videoPlayerControls-btn-icon_screenExpand{
	background-position: 0 -48px;
}
.videoPlayerControls-btn-icon_screenContract{
	background-position: 0 -72px;
}
.videoPlayerControls-timeline{
	-webkit-box-flex: 1;
	-moz-box-flex: 1;
	-ms-box-flex: 1;
	-o-box-flex: 1;
	box-flex: 1;
	-webkit-flex:1;
	-moz-flex:1;
	-ms-flex:1;
	-o-flex:1;
	flex:1;
	padding: 20px;
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
	float: left;

}
.videoPlayerControls-progressWrap{
	position: relative;
	background-color: rgba(255,255,255,.5);
	-webkit-box-shadow: 0 0 10px rgba(0,0,0,.4) inset;
	-moz-box-shadow: 0 0 10px rgba(0,0,0,.4) inset;
	box-shadow: 0 0 10px rgba(0,0,0,.4) inset;
	cursor: pointer;
	-moz-user-select: none; /*火狐*/
	-webkit-user-select: none;  /*webkit浏览器*/
	-ms-user-select: none;   /*IE10*/
	-khtml-user-select: none; /*早期浏览器*/
	user-select: none;
}
.videoPlayerControls-progressWrap:hover{
	-webkit-box-shadow: 0 0 10px rgba(0,0,0,.4);
	-moz-box-shadow: 0 0 10px rgba(0,0,0,.4);
	box-shadow: 0 0 10px rgba(0,0,0,.4);
}
.videoPlayerControls-progressWrap:hover .videoPlayerControls-progress{
	background-color: rgba(0,0,0,.6);
}
.videoPlayerControls-progress{
	position: absolute;
	z-index: 2;
	background-color: rgba(0,0,0,.5);
}
._videoPlayerControls-timelineProgressWrap{
	width: 100%;
	height: 10px;
}
._videoPlayerControls-timelinePlayProgress,._videoPlayerControls-timelineBufferProgress{
	width: 0%;
	height: 10px;
}
._videoPlayerControls-timelineBufferProgress{
	width: 100%;
	background-color: rgba(0,0,0,.2);
	z-index: 1;
}
.videoPlayerControls-audioVolume{
	display: none;
	padding: 10px 22px;
	position: absolute;
	top: -100px;
	left: 0;
	background-color: rgba(255,255,255,.2);
}
._videoPlayerControls-audioBtn:hover .videoPlayerControls-audioVolume{
	display: block;
}
._videoPlayerControls-volumeProgressWrap{
	width: 6px;
	height: 80px;
	left: 0;
	top: 0;
}
._videoPlayerControls-volumeProgress{
	width: 6px;
	height: 20%;
	bottom: 0;
	left: 0;
}

 

最最最后想说——现在越来越懒得给代码加注释了,不过我感觉看变量名应该就能明白它是干吗用的。另外,对于VideoPlayer类的模式,应该除prototype外还有更高效的写法,望高手不鄙赐教。

呃……凌晨一点半,我要睡了……