更新:

关于第二点,也就是说计算进度条拖放按钮定位的问题。

很感谢 batsing 同学提供了更好的方案: 滑块左偏量 = (进度条长 - 滑块长) * (已播时间/总时长)

尝试过之后发现除了拖曳滑片的时候会抛锚外,其它暂时没发现什么问题,并且较之前的算法省了很多不必要的步骤,所以如今除了拖曳操作的时候使用旧方法外,已经进行修改。如果还有其它建议欢迎告诉我~~(づ ̄ 3 ̄)づ

正文:

本以为写一个video播放器不难,可写着写着坑就越挖越大了。

先看一下播放器大概是长这样的:

在开发过程中这几个问题深深地困扰着我:

  1、各机器兼容问题

  2、关于视频播放过程中进度条显示情况

  3、关于改变播放进度和音量大小的操作问题

  

1、各机器兼容问题

  除了chrome很乖支持良好外,其中UC获取不到duration属性,也就获取不到视频的总长度了,但这部分可以由后台传参数解决,但是不能禁用系统播放器这个就不太好了。。。。

  抛开其它浏览器,来看看腾讯X5内核的浏览器的支持情况:http://x5.tencent.com/guide?id=2009

  解决剩下问题前先了解一下video中常用的一些方法、属性和事件:

  duration:返回当前音频/视频的长度(以秒计)

  currentTime:设置或返回音频/视频中的当前播放位置(以秒计)

  play(): 开始播放音频/视频

  pause(): 暂停当前播放的音频/视频

  监听timeupdate事件,获得当前视频播放的进度。

嗯,了解了这些之后也大概明白播放器是怎样工作的了,看着API撸代码就行了:http://www.w3school.com.cn/tags/html_ref_audio_video_dom.asp

2、关于视频播放过程中进度条显示情况 (主要指的是:进度条和拖放按钮需要按照视频的播放情况而进行调整的问题。)

  本来进度条的计算方法是显而易见的:

  已播放进度 = 进度条长度 / (总时长 / 已播放时间)

  但是因为进度条多了一个拖放按钮,而且拖放按钮的定位是跟随视频的播放而改变的。

  如果按照上面的算法,很有可能在视频播放完成的时候,拖放按钮会超出进度条的范围,变成了这样:

  

  也考虑过当按钮的宽度 + 按钮的position.left >= 进度条长度的时候,拖放按钮不再移动。但这样就又产生了一个问题了:视频并未播放完,但拖放按钮已经到了尽头,很容易给用户造成以为视频已经播放完毕的错觉:

  ( 在已播放时间 < 总时长的情况下,拖放按钮已经到尽头了)

  花了好多时间,地思来想去才灵光一闪想到解决方案:

    为啥不在视频播放过程中把正在向前移动的按钮一点点地往后退呢:

  是时候展示我高超的画技了(*^__^*) 嘻嘻……

  如上图所示,涂黑的部分根据百分比慢慢地增加(为方便起见给上图中涂黑的部分设一个变量: iconDetract;)根据视频已播放的时间占总时长的百分比,计算出占按钮宽度的百分比iconDetract。然后再用按钮原来的position.left - iconDetract,得出按钮的实际定位。

  由于一点点地减去iconDetract,直到iconDetract == 按钮的总宽度为止。(这时候视频也播放完毕了):

  先计算出 按钮原来的定位(activeTol) = 进度条长度 / (总时长 / 已播放时间)

  再根据视频的播放进度计算出 iconDetract = 按钮宽度 / (总时长 / 已播放时间)

  最后得出 按钮的实际定位 = activeTol - iconDetract

  (嗯,第一个问题就这样妥妥地解决掉了,目前只想到了这么一个解决方法。如果你有更好的方法,请告诉我。 )

3、关于改变播放时间点和音量大小的操作问题

  改变视频的时间点有两种方法:

    1:拖动拖放按钮,慢慢改变视频的播放时间点(慢)

    2:点击进度条的某一个位置,让视频直接跳到相对应的时间点(快)

  改变音量的两种方法:

    1:拖动拖放按钮,一点点慢慢改变音量的大小(慢)

    2:点击音量条的某一个位置,让音量直接跳到相对应的位置上(快)

  (它们的共同的地方都有拖动按钮操作,以及点击进度条,按钮直接到相对于的位置上。唯一不同的是,操作的是视频的进度条抑或是音量条。)

  怎样可以各自调用一套代码实现各自不同的功能咧,这是目前所要解决的。

  实现方法并不难,只是繁琐。

  因为拖放按钮的时候需要先点击拖放按钮,但由于冒泡机制,会同时点击进度条。这时候是就会把上面列出来的1和2的操作都一起做了,并不符合现实所需。

  所以,调用的方法的时候传一个参数moveing,当moveing == true的时候,当前对象是按钮,而不是它的父级进度条,并且阻止冒泡 e.stopPropagation();

  但由于拖放按钮,和点击进度条使得改变视频的播放时间点以及改变音量,需要获取到焦点的X、Y坐标,这时候除了区分当前操作的是视频的进度抑或是调整音量的大小外还需要区分this的指向到底是当前操作对象还是它的父级。(代码中liveEvent部分)

  

demo完整代码如下

css:

1     <style type="text/css">
2     .control_bar{background-color:black;height:30px;line-height:30px;}
3     .control_bar span{ height:20px;line-height:20px; vertical-align:middle; display:inline-block;background-color:#fff;opacity:0.8;margin:0 10px;cursor:pointer;}
4     .control_bar #progress_bar,.control_bar #volume_bar{width:32%;border-radius:5px;height:10px; line-height:10px; position:relative;}
5     #progress_bar em,#volume_bar em{background-color:#5CA66A;height:100%; display:inline-block;}
6     .control_bar #time{color:#fff;font-size:14px;background:none;}
7     .control_bar #volume_bar{width:100px;}
8     #progress_bar_icon,#volume_bar_icon{position:absolute;top:-3px;left:0px; border-radius:50%;height:20px;width:20px;display:block;background:#964E4E;}
9     </style>

View Code

html:

 1 <body>
 2     <div style="width:600px;">
 3         <div class="videoPlayer" id="videoContainer">
 4             <video id="video" width="600" height="360">
 5             <source src="test.mp4" type='video/mp4'>
 6             </video>
 7             <div class="control_bar">
 8                 <span id="pause">暂停</span>
 9                 <span id="progress_bar"><em></em><i id="progress_bar_icon"></i></span>
10                 <span id="time"></span>
11                 <span id="volume">音量</span>
12                 <span id="volume_bar"><em></em><i id="volume_bar_icon"></i></span>
13                 <span id="fullscreen">放大</span>
14             </div>
15         </div>
16     </div>
17 <script type="text/javascript" src="index.js"></script>
18 <script type="text/javascript">
19
20 var videoSet = new VideoContainer({
21         volumeTol: 0.5, //默认音量
22         video: document.getElementById("video"),
23         pause: document.getElementById("pause"),
24         timer: document.getElementById("time"),
25         progressBar: document.getElementById("progress_bar"),
26         progressBarIcon:document.getElementById("progress_bar_icon"),
27         volume: document.getElementById("volume"),
28         volumeBar: document.getElementById("volume_bar"),
29         volumeBarIcon:document.getElementById("volume_bar_icon"),
30         fullscreen: document.getElementById("fullscreen")
31     });
32
33 </script>
34 </body>

View Code

js:

'use strict';//检测设备类型
var startWhen, endWhen, moveWhen;
var u = navigator.userAgent; if ( u.match(/\b(Windows\sNT|Macintosh)\b/) ) {// 鼠标startWhen = 'mousedown';endWhen = 'mouseup';moveWhen = 'mousemove';
} else {// 触摸屏startWhen = 'touchstart';endWhen = 'touchend';moveWhen = 'touchmove';
}// 原生的JavaScript事件绑定函数
function bindEvent(ele, eventName, func){if(window.addEventListener){ele.addEventListener(eventName, func);}else{ele.attachEvent('on' + eventName, func);}
}function VideoContainer(options) {var t = this;t.volumeTol = options.volumeTol;t.video = options.video;t.pauseBtn = options.pause;t.progressBar = options.progressBar;t.timer = options.timer;t.volume = options.volume;t.volumeBar = options.volumeBar;t.volumeBarIcon = options.volumeBarIcon;t.progressBarIcon = options.progressBarIcon;t.fullScreenBtn = options.fullscreen;t.activeTol = '';//载入视频//获取视频总时长t.video.addEventListener("canplaythrough", function(){t.videoDuration = t.video.duration;t.tolTime = t.videoTime(t.videoDuration); t.timer.innerHTML = '00:00/' + t.tolTime.minutes_str + ':' + t.tolTime.seconds_str;});//播放时间点更新t.video.addEventListener("timeupdate", function(){t.timeupdate = t.videoTime(t.video.currentTime);//更新时间t.timer.innerHTML = t.timeupdate.minutes_str + ':' + t.timeupdate.seconds_str + '/' + t.tolTime.minutes_str + ':' + t.tolTime.seconds_str;t.activeTol = (t.progressBar.offsetWidth  - t.progressBarIcon.offsetWidth) * ( t.video.currentTime / t.videoDuration );t.progressBarIcon.style.left = t.activeTol + 'px';t.progressBar.children[0].style.width = t.activeTol + 'px';});//设置默认音量t.volumeTol = t.volumeTol ? t.volumeTol : 0.5;t.video.volume = t.volumeTol;t.volumeBar.children[0].style.width = t.volumeTol * 100 + '%';t.volumeBarIcon.style.left = t.volumeTol * 100 + '%';//各绑定事件//暂停t.pauseBtn.onclick = function() {t.videoPaused();}//音量t.volume.onclick = function() {t.videoMuted();}//全屏t.fullScreenBtn.onclick = function() {t.videoFullscreen(this);}t.liveEvent.call(t.progressBar, { _this: this, attribute: 'progressBar', moveing: false});t.liveEvent.call(t.volumeBar, { _this: this, attribute: 'volumeBar', moveing: false});t.liveEvent.call(t.progressBarIcon, { _this: this, attribute: 'progress_bar_icon', moveing: true});t.liveEvent.call(t.volumeBarIcon, { _this: this, attribute: 'volume_bar_icon', moveing: true});bindEvent(document, endWhen, function(e) {t._draging = false;});
}VideoContainer.prototype.liveEvent = function(options) {var t = options._this;//区分当前操作元素是拖动按钮还是它的父级var _this = options.moveing ? this.parentNode : this;var _parentWidth = _this.offsetWidth;//检测设备类型var _ua = function(e) {var Pos = null;if ( u.match(/\b(Windows\sNT|Macintosh)\b/) ) {e = e || window.event;Pos = {left : e.pageX,top: e.pageY}} else {var touch =  e.targetTouches[0] || e.changedTouches[0]Pos = {left : touch.pageX ,top: touch.pageY}}return Pos;};//区分拖动的是进度条还是音量条function playStep() {if (options.attribute == 'progress_bar_icon' || options.attribute == 'progressBar') {if(options.moveing === true) {//根据拖放的进度计算相对于占icon宽的多少var init = t.progressBarIcon.offsetWidth / (_parentWidth / t._durCount.left);var progressBarIconWidth = t.progressBarIcon.offsetWidth;//拖放按钮是否超出范围if ( t._durCount.left + progressBarIconWidth - init >= _parentWidth ){t.video.currentTime = t.videoDuration;t.progressBar.children[0].style.width = _parentWidth + 'px';t.progressBarIcon.style.left = _parentWidth - progressBarIconWidth + 'px';} else{t.video.currentTime = t.videoDuration / (_parentWidth / (t._durCount.left + init));t.progressBar.children[0].style.width = t._durCount.left + 'px';t.progressBarIcon.style.left = t._durCount.left + 'px';}} else{t.activeTol = (t.progressBar.offsetWidth  - progressBarIconWidth) * ( t._durCount.left / _parentWidth );t.progressBar.children[0].style.width = t._durCount.left + 'px';t.video.currentTime = t.videoDuration / (_parentWidth / t._durCount.left);t.progressBarIcon.style.left = t.activeTol + 'px';} } else{if ( t._durCount.left + t.volumeBarIcon.offsetWidth >= _parentWidth ){t.volumeTol = 1;} else if( t._durCount.left <= 0 ){t.volumeTol = 0;} else {t.volumeTol = 1 / (_parentWidth / t._durCount.left);}//拖放按钮是否超出范围if (t.volumeTol == 1) {t.volumeBarIcon.style.left = _parentWidth - t.volumeBarIcon.offsetWidth + 'px';} else{t.volumeBarIcon.style.left = t.volumeTol * 100 + '%';}t.video.volume = t.volumeTol;t.volumeBar.children[0].style.width = t.volumeTol * 100 + '%';}}//仅限拖动按钮可移动if ( options.moveing === true) {bindEvent(this, moveWhen, function(e) {if (t._draging) {//鼠标移动了多少距离t._mousePos = {left: (_ua(e).left -  _this.offsetLeft) - t._startPos.left,top: (_ua(e).top - _this.offsetTop) - t._startPos.top}t._motion = true;//鼠标是否在拖动范围内if (0 <= t._mousePos.left <= _parentWidth || 0 < t._mousePos.top <= _this.offsetHeight){//移动后的坐标 = 上次记录的定位 + 鼠标移动了的距离;t._durCount = {left: t._oldPos.left + t._mousePos.left,top: t._oldPos.top + t._mousePos.top,};playStep();} else {t._draging = false;}}}); }bindEvent(this, startWhen, function(e) {// 防止选择文字、拖动页面(触摸屏)鼠标移动过快松开抛锚
        e.preventDefault();if ( this.setCapture ) {this.setCapture();}//如果当前对象是拖动按钮,阻止冒泡if (options.moveing === true) {e.stopPropagation();}t._draging = true;t._motion = false;//记录按下鼠标到背景图片的距离t._startPos = {left: _ua(e).left -  _this.offsetLeft,top: _ua(e).top - _this.offsetTop}//当前拖动按钮的定位t._oldPos = {left: this.offsetLeft,top: this.offsetTop};//本次拖动的距离t._durCount = t._startPos;});bindEvent(this, endWhen, function(e) {t._draging = false;//如果进行的是拖动操作,则不必再更新视频的进度if( !t._motion) {playStep();}// 防止选择文字、拖动页面(触摸屏)鼠标移动过快松开抛锚if (this.releaseCapture) {this.releaseCapture();}delete t._draging; //是否拖动delete t._startPos; //鼠标按下坐标delete t._oldPos; //拖动按钮的定位delete t._mousePos; //鼠标移动的距离
    });
}
//转换时间单位
VideoContainer.prototype.videoTime = function(time) {var timeId = {}var seconds = time > 0 ? parseInt(time) : 0;var minutes = parseInt(time / 60);seconds = seconds - minutes * 60;timeId.minutes_str = minutes < 10 ? '0' + minutes : minutes;timeId.seconds_str = seconds < 10 ? '0' + seconds : seconds;return timeId;
}//是否全屏
VideoContainer.prototype.videoFullscreen = function() {var t = this;var element = t.video;//反射調用var invokeFieldOrMethod = function(element, method) {var usablePrefixMethod;["webkit", "moz", "ms", "o", ""].forEach(function(prefix) {if (usablePrefixMethod) return;if (prefix === "") {// 无前缀,方法首字母小写method = method.slice(0,1).toLowerCase() + method.slice(1);   }var typePrefixMethod = typeof element[prefix + method];if (typePrefixMethod + "" !== "undefined") {if (typePrefixMethod === "function") {usablePrefixMethod = element[prefix + method]();} else {usablePrefixMethod = element[prefix + method];}}});return usablePrefixMethod;};if( invokeFieldOrMethod(document,'FullScreen') || invokeFieldOrMethod(document,'IsFullScreen') || document.IsFullScreen ){//退出全屏if ( document.exitFullscreen ) {document.exitFullscreen();} else if ( document.msExitFullscreen ) {document.msExitFullscreen();} else if ( document.mozCancelFullScreen ) {document.mozCancelFullScreen();} else if( document.oRequestFullscreen ){document.oCancelFullScreen();} else if ( document.webkitExitFullscreen ){document.webkitExitFullscreen();} else{var docHtml  = document.documentElement;var docBody  = document.body;var videobox  = element.parentNode;docHtml.style.cssText = "";docBody.style.cssText = "";videobox.style.cssText = "";document.IsFullScreen = false;}} else {//進入全屏//此方法不可以在異步任務中執行,否則火狐無法全屏if(element.requestFullscreen) {element.requestFullscreen();} else if(element.mozRequestFullScreen) {element.mozRequestFullScreen();} else if(element.msRequestFullscreen){ element.msRequestFullscreen();  } else if(element.oRequestFullscreen){element.oRequestFullscreen();} else if(element.webkitRequestFullscreen){element.webkitRequestFullScreen();}else{var docHtml  = document.documentElement;var docBody  = document.body;var videobox  = element.parentNode;var  cssText = 'width:100%;height:100%;overflow:hidden;';docHtml.style.cssText = cssText;docBody.style.cssText = cssText;videobox.style.cssText = cssText+';'+'margin:0px;padding:0px;';document.IsFullScreen = true;}}
}
//是否暂停
VideoContainer.prototype.videoPaused = function() {var t = this;if(t.video.paused) {t.video.play();} else{t.video.pause();}
}
//调整音量
VideoContainer.prototype.videoMuted = function() {var t = this;if ( !t.video.defaultMuted ){t.video.volume = 0;t.video.defaultMuted = true;t.volumeBar.children[0].style.width = 0 + '%';t.volumeBarIcon.style.left = 0 + '%';} else {t.video.volume = t.volumeTol;t.video.defaultMuted = false;t.volumeBar.children[0].style.width = t.volumeTol * 100 + '%';//拖放按钮是否超出范围if (t.volumeTol == 1) {t.volumeBarIcon.style.left = t.volumeBar.offsetWidth - t.volumeBarIcon.offsetWidth + 'px';} else{t.volumeBarIcon.style.left = t.volumeTol * 100 + '%';}}
}

View Code

转载于:https://www.cnblogs.com/Travel/p/5396402.html

模拟video播放器相关推荐

  1. 微信中H5同层Video播放器接入教程

    微信中H5同层Video播放器接入教程 x5-video-player-type 启用H5同层播放器 通过video属性"x5-video-player-type"声明启用同层H5 ...

  2. 玩转前端 Video 播放器

    Web 开发者们一直以来想在 Web 中使用音频和视频,但早些时候,传统的 Web 技术不能够在 Web 中嵌入音频和视频,所以一些像 Flash.Silverlight 的专利技术在处理这些内容上变 ...

  3. 【Web技术】662- 玩转前端 Video 播放器

    Web 开发者们一直以来想在 Web 中使用音频和视频,但早些时候,传统的 Web 技术不能够在 Web 中嵌入音频和视频,所以一些像 Flash.Silverlight 的专利技术在处理这些内容上变 ...

  4. js-前端video播放器

    js-前端video播放器 目录 文章目录 前言 推荐阅读 传统播放模式 视频切片 - `Accept-Ranges` 服务器端请求特定的范围 单一范围 多重范围 条件式范围请求 范围请求的响应 流媒 ...

  5. H5 实现自定义video播放器,快来点我吧

    效果 要实现自定义video播放器需要熟悉video的相关操作 视频播放它有哪些操作         1. 播放 play()         2. 暂停 pause()         3. 判断当 ...

  6. video 满屏显示_HTML5 video播放器全屏(fullScreen)实现的方法

    这篇文章主要介绍了HTML5 video播放器全屏(fullScreen)方法实例,本文直接给出一个完整代码实例,需要的朋友可以参考下 首先来说,这个标题具有误导性,但这样设置改标题也是主要因为vid ...

  7. JS 用CANVAS自定义VIDEO播放器

    JS 用CANVAS自定义VIDEO播放器 概述 CANVAS绘制核心代码 播放器代码 使用页面HTML代码 使用页面JS代码 使用示例 概述 HTML5用规范的VIDEO标签取代了以往需要借助外部控 ...

  8. html5的video播放器上禁止下载和禁止右键下载实现。

    <video id="video" src="#" controls controlsList="nodownload" oncont ...

  9. 苹果 ios 使用video 播放器,怎么禁止自动全屏

    苹果 ios 使用video 播放器,怎么禁止自动全屏 最近在做兼职,遇到两个小问题,记录下, ios系统怎么在微信中禁用video 全屏展示 在video标签中 增加 x5-video-player ...

最新文章

  1. 【BZOJ1305】 [CQOI2009]dance跳舞
  2. 人工智能改进传统云ERP的10种方法
  3. 下拉框处理(select)
  4. 实现分页统计记录总数时: sql语句的异常
  5. 微软低头,Chrome 一统浏览器!
  6. 凸优化有关的数值线性代数知识二:求解已经因式分解的矩阵的线性方程组
  7. MTRR内存类型范围寄存器
  8. jquery文档modal_jQuery代码文档小工具
  9. 使用USB转485编程电缆连接西门子 S7-200的软件配置方法以及可能出现的问题
  10. 浅谈登录服务器的方法
  11. win7安装计算机的更新,解决win7系统更新升级教程
  12. (五)可重入锁ReentrantLock的底层原理实现?
  13. Adobe将支持HTTP流媒体直播 预示着ipad将可以用flash吗?
  14. MES管理系统应用环境分析,以及实施面临的挑战
  15. mac怎么搭建网站服务器,mac搭建本地服务器(示例代码)
  16. 平衡二叉树、B树、B+树,B*树的区别与联系
  17. 那天是一年的第几天?
  18. 用vlookup在excel表格里查找数据
  19. linux硬件设备操作函数 open(/dev/ietctl, O_RDWR|...)
  20. 解决无法使用localhost连接数据库,使用服务器IP可以

热门文章

  1. 在IE中使用Firebug
  2. 笔记本电脑按开机键没反应怎么办?(先记得长按开机键,大约10秒钟看看可以吗)
  3. 算法分析之-渐进记号
  4. Wonder 1.0 正式版发布,WebGL 3D引擎和编辑器
  5. dubbo自定义异常传递信息丢失问题解决
  6. vim 多行注释消除注释,多行删除
  7. 网页3D效果库Three.js学习[二]-了解照相机
  8. 搭建通用性多用户后台-思路
  9. 记录一个班级的成绩练习一维数组
  10. Node.js与Express4安装与配置