本文使用的都是CSS3以及html5中的video实现的播放器,在PC与手机端都可以完美播放,目前还未实现播放进度条的拖移功能,后续会增加这个功能。
demo效果查看地址:https://king2088.github.io/html5-vPlayer/
先看效果图:
首先我们要了解一下html5中的media事件以及属性
html5 Media api
*loadstart
*progress
*suspend
*abort
*error
*emptied
*stalled
*loadedmetadata
*loadeddata
*canplay
*canplaythrough
*playing
*waiting
*seeking
*seeked
*ended
*durationchange
*timeupdate
*play
*pause
*ratechange
*resize
*volumechange
Media properties
[ “error”, “src”, “srcObject”, “currentSrc”, “crossOrigin”, “networkState”, “preload”, “buffered”, “readyState”, “seeking”, “currentTime”, “duration”, “paused”, “defaultPlaybackRate”, “playbackRate”, “played”, “seekable”, “ended”, “autoplay”, “loop”, “controls”, “volume”, “muted”, “defaultMuted”, “audioTracks”, “videoTracks”, “textTracks”, “width”, “height”, “videoWidth”, “videoHeight”, “poster” ]
3、CSS部分代码
1 | *, |
2 | html, |
3 | body, |
4 | video { |
5 | margin: 0; |
6 | padding: 0; |
7 | } |
8 | * #vPlayer, |
9 | html #vPlayer, |
10 | body #vPlayer, |
11 | video #vPlayer { |
12 | width: 100%; |
13 | background: #000; |
14 | position: relative; |
15 | overflow: hidden; |
16 | } |
17 | * #playerContent, |
18 | html #playerContent, |
19 | body #playerContent, |
20 | video #playerContent { |
21 | width: 100%; |
22 | background: #000; |
23 | display: block; |
24 | } |
25 | * #playerControler, |
26 | html #playerControler, |
27 | body #playerControler, |
28 | video #playerControler { |
29 | width: 100%; |
30 | z-index: 99999999; |
31 | position: absolute; |
32 | bottom: 2px; |
33 | left: 0; |
34 | padding: 4px 0; |
35 | opacity: 1; |
36 | } |
37 | * #playerControler:after, |
38 | html #playerControler:after, |
39 | body #playerControler:after, |
40 | video #playerControler:after { |
41 | content: ''; |
42 | display: block; |
43 | height: 0; |
44 | clear: both; |
45 | } |
46 | * #playerControler .playButtonWrap, |
47 | html #playerControler .playButtonWrap, |
48 | body #playerControler .playButtonWrap, |
49 | video #playerControler .playButtonWrap { |
50 | width: 24px; |
51 | height: 24px; |
52 | border-radius: 14px; |
53 | border: 2px #ccc solid; |
54 | margin-left: 8px; |
55 | float: left; |
56 | } |
57 | * #playerControler .playButtonWrap .playButton, |
58 | html #playerControler .playButtonWrap .playButton, |
59 | body #playerControler .playButtonWrap .playButton, |
60 | video #playerControler .playButtonWrap .playButton { |
61 | margin: 4px 0 0 8px; |
62 | width: 0; |
63 | height: 0; |
64 | border-bottom: 8px solid transparent; |
65 | border-top: 8px solid transparent; |
66 | border-left: 10px solid #ccc; |
67 | } |
68 | * #playerControler .playButtonWrap .pauseButton, |
69 | html #playerControler .playButtonWrap .pauseButton, |
70 | body #playerControler .playButtonWrap .pauseButton, |
71 | video #playerControler .playButtonWrap .pauseButton { |
72 | margin: 6px 0 0 8px; |
73 | width: 5px; |
74 | height: 12px; |
75 | border-left: 2px #ccc solid; |
76 | border-right: 2px #ccc solid; |
77 | display: none; |
78 | } |
79 | * #playerControler #vplayerTimeLine, |
80 | html #playerControler #vplayerTimeLine, |
81 | body #playerControler #vplayerTimeLine, |
82 | video #playerControler #vplayerTimeLine { |
83 | color: #ccc; |
84 | line-height: 30px; |
85 | font-size: 12px; |
86 | float: left; |
87 | margin-left: 2%; |
88 | } |
89 | * #playerControler .vPlayVolumeWrap, |
90 | html #playerControler .vPlayVolumeWrap, |
91 | body #playerControler .vPlayVolumeWrap, |
92 | video #playerControler .vPlayVolumeWrap { |
93 | float: right; |
94 | margin: 6px 10% 0 0; |
95 | width: 25%; |
96 | } |
97 | * #playerControler .vPlayVolumeWrap #volumeIcon, |
98 | html #playerControler .vPlayVolumeWrap #volumeIcon, |
99 | body #playerControler .vPlayVolumeWrap #volumeIcon, |
100 | video #playerControler .vPlayVolumeWrap #volumeIcon { |
101 | width: 0px; |
102 | height: 0px; |
103 | padding: 5px 3px; |
104 | border-width: 5px 10px 5px 0px; |
105 | border-color: transparent #ccc transparent transparent; |
106 | border-style: solid; |
107 | /*喇叭效果*/ |
108 | -webkit-box-shadow: inset 15px 0 #666; |
109 | -moz-box-shadow: inset 15px 0 #666; |
110 | box-shadow: inset 15px 0 #666; |
111 | float: left; |
112 | display: inline-block; |
113 | } |
114 | * #playerControler .vPlayVolumeWrap #vPlayVolume, |
115 | html #playerControler .vPlayVolumeWrap #vPlayVolume, |
116 | body #playerControler .vPlayVolumeWrap #vPlayVolume, |
117 | video #playerControler .vPlayVolumeWrap #vPlayVolume { |
118 | width: 70%; |
119 | height: 2px; |
120 | background: #ccc; |
121 | position: relative; |
122 | float: left; |
123 | margin: 10px 0 0 10px; |
124 | display: inline-block; |
125 | } |
126 | * #playerControler .vPlayVolumeWrap #vPlayVolume #dragBar, |
127 | html #playerControler .vPlayVolumeWrap #vPlayVolume #dragBar, |
128 | body #playerControler .vPlayVolumeWrap #vPlayVolume #dragBar, |
129 | video #playerControler .vPlayVolumeWrap #vPlayVolume #dragBar { |
130 | width: 8px; |
131 | height: 8px; |
132 | border-radius: 4px; |
133 | background: #369; |
134 | position: absolute; |
135 | top: -3px; |
136 | left: 0; |
137 | cursor: pointer; |
138 | } |
139 | * #playerControler .vPlayVolumeWrap #vPlayVolume #dragMask, |
140 | html #playerControler .vPlayVolumeWrap #vPlayVolume #dragMask, |
141 | body #playerControler .vPlayVolumeWrap #vPlayVolume #dragMask, |
142 | video #playerControler .vPlayVolumeWrap #vPlayVolume #dragMask { |
143 | position: absolute; |
144 | left: 0; |
145 | top: 0; |
146 | background: #369; |
147 | width: 0; |
148 | height: 2px; |
149 | } |
150 | * #playerControler #fullScreen, |
151 | html #playerControler #fullScreen, |
152 | body #playerControler #fullScreen, |
153 | video #playerControler #fullScreen { |
154 | width: 24px; |
155 | height: 24px; |
156 | float: right; |
157 | margin: 5px 2% 0 0; |
158 | display: block; |
159 | background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAA/UlEQVRIS91V2w3CMAy8qP0HNoAJoOrtQ9mgI8AEiAlgBAZxJEaADWCBGqUqUktbkSAVAfmMH+ezk7PBwMcMnB+fB7DW5kVRLI0xixq7K8lJna2IXAGMandnVT3GcbxJksTZytNg4JKr6rajbTeS4xcAD/OOZN4JICInAHNjzCpN00PIfGrFnUnO+gDUGUi+NRsRacU3EnU5hLD4CoBy+s8D9WVRvaxG/Fu99gVsPdOQQF/fP2NQDUmfZcG3HV3x//cPBpeKUuwA5CR3vr13ftbaTFX3AC4kp6Fy7bMPXst1VYnbCVnF5BHksw8uAI5RFK17F05IS3x9f/8n3wFQDZcZDw/gGQAAAABJRU5ErkJggg==") no-repeat center; |
160 | } |
161 | * #progressBar, |
162 | html #progressBar, |
163 | body #progressBar, |
164 | video #progressBar { |
165 | width: 100%; |
166 | background: #333; |
167 | height: 2px; |
168 | position: absolute; |
169 | bottom: 0; |
170 | display: block; |
171 | } |
172 | * #progressBar .reloadProgress, |
173 | html #progressBar .reloadProgress, |
174 | body #progressBar .reloadProgress, |
175 | video #progressBar .reloadProgress { |
176 | width: 0; |
177 | height: 2px; |
178 | background: #ccc; |
179 | position: relative; |
180 | left: 0; |
181 | top: 0; |
182 | z-index: 999; |
183 | } |
184 | * #progressBar .reloadProgress .progress, |
185 | html #progressBar .reloadProgress .progress, |
186 | body #progressBar .reloadProgress .progress, |
187 | video #progressBar .reloadProgress .progress { |
188 | width: 0; |
189 | height: 2px; |
190 | background: #008b8b; |
191 | position: absolute; |
192 | left: 0; |
193 | top: 0; |
194 | z-index: 9999; |
195 | } |
196 | * .fadeIn, |
197 | html .fadeIn, |
198 | body .fadeIn, |
199 | video .fadeIn { |
200 | animation-name: fadeIn; |
201 | } |
202 | * .fadeOut, |
203 | html .fadeOut, |
204 | body .fadeOut, |
205 | video .fadeOut { |
206 | animation-name: fadeOut; |
207 | } |
208 | @-moz-keyframes fadeIn { |
209 | from { |
210 | opacity: 0; |
211 | } |
212 | to { |
213 | opacity: 1; |
214 | } |
215 | } |
216 | @-webkit-keyframes fadeIn { |
217 | from { |
218 | opacity: 0; |
219 | } |
220 | to { |
221 | opacity: 1; |
222 | } |
223 | } |
224 | @-o-keyframes fadeIn { |
225 | from { |
226 | opacity: 0; |
227 | } |
228 | to { |
229 | opacity: 1; |
230 | } |
231 | } |
232 | @keyframes fadeIn { |
233 | from { |
234 | opacity: 0; |
235 | } |
236 | to { |
237 | opacity: 1; |
238 | } |
239 | } |
240 | @-moz-keyframes fadeOut { |
241 | from { |
242 | opacity: 1; |
243 | } |
244 | to { |
245 | opacity: 0; |
246 | } |
247 | } |
248 | @-webkit-keyframes fadeOut { |
249 | from { |
250 | opacity: 1; |
251 | } |
252 | to { |
253 | opacity: 0; |
254 | } |
255 | } |
256 | @-o-keyframes fadeOut { |
257 | from { |
258 | opacity: 1; |
259 | } |
260 | to { |
261 | opacity: 0; |
262 | } |
263 | } |
264 | @keyframes fadeOut { |
265 | from { |
266 | opacity: 1; |
267 | } |
268 | to { |
269 | opacity: 0; |
270 | } |
271 | } |
272 | video::-webkit-media-controls, |
273 | video::-moz-media-controls, |
274 | video::-webkit-media-controls-enclosure { |
275 | display: none ; |
276 | } |
277 | video::-webkit-media-controls-panel, |
278 | video::-webkit-media-controls-panel-container, |
279 | video::-webkit-media-controls-start-playback-button { |
280 | display: none ; |
281 | -webkit-appearance: none; |
282 | } |
4、html部分代码
1 |
|
2 | <html lang="en"> |
3 | <head> |
4 | <meta charset="UTF-8"> |
5 | <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> |
6 | <title>vPlayer</title> |
7 | <link rel="stylesheet" href="css/style.css"> |
8 | |
9 | </head> |
10 | <body> |
11 | <div id="vPlayer"> |
12 | <video id="playerContent"> |
13 | <!--<source src="http://media.w3.org/2010/05/sintel/trailer.webm" type='video/webm;codecs="vp8, vorbis"'/>--> |
14 | <source src="http://media.w3.org/2010/05/bunny/movie.mp4" type='video/mp4;codecs="avc1.42E01E, mp4a.40.2"'/> |
15 | </video> |
16 | <div id="playerControler"> |
17 | <div class="playButtonWrap"> |
18 | <div id="play" class="playButton"></div> |
19 | <div id="pause" class="pauseButton"></div> |
20 | </div> |
21 | <div id="vplayerTimeLine"></div> |
22 | <div id="fullScreen"></div> |
23 | <div class="vPlayVolumeWrap"> |
24 | <div id="volumeIcon"></div> |
25 | <div id="vPlayVolume"> |
26 | <div id="dragBar"></div> |
27 | <div id="dragMask"></div> |
28 | </div> |
29 | </div> |
30 | |
31 | </div> |
32 | <div id="progressBar"> |
33 | <div class="reloadProgress"> |
34 | <div class="progress"></div> |
35 | </div> |
36 | </div> |
37 | </div> |
38 | <script src="js/man.js"></script> |
39 | </body> |
40 | </html> |
5、javascript部分代码
1 |
|
2 | window.onload = function () { |
3 | var vPlayer = document.getElementById('vPlayer') |
4 | var playerContent = document.getElementById('playerContent') |
5 | var playerControl = document.getElementById('playerControler') |
6 | var play = document.getElementById('play') |
7 | var pause = document.getElementById('pause') |
8 | var reloadProgressDOM = document.getElementById('progressBar') |
9 | var progressTimeShowDOM = document.getElementById('vplayerTimeLine') |
10 | var volumeIconDOM = document.getElementById('volumeIcon') |
11 | var vPlayVolumeDOM = document.getElementById('vPlayVolume') |
12 | var dragBarDOM = document.getElementById('dragBar') |
13 | var dragMaskDOM = document.getElementById('dragMask') |
14 | var fullscreen = document.getElementById('fullScreen') |
15 | var _playerControlHidden,_playTime |
16 | |
17 | var isSupportTouch = 'ontouchend' in document ? true : false; |
18 | // console.log('isSupportTouc',isSupportTouch) |
19 | playerContent.controls = false |
20 | playerContent.preload = 'auto' |
21 | |
22 | //3000ms hidden player |
23 | playerControlHidden() |
24 | //play |
25 | play.onclick = function () { |
26 | vPlayerPlay() |
27 | } |
28 | // play.ontouchstart = function () { |
29 | // vPlayerPlay() |
30 | // } |
31 | |
32 | //pause |
33 | pause.onclick = function () { |
34 | vPlayerPause() |
35 | } |
36 | // pause.ontouchstart = function () { |
37 | // vPlayerPause() |
38 | // } |
39 | //show player control |
40 | if(!isSupportTouch){ |
41 | playerContent.addEventListener('mouseover',playerControlShow) |
42 | }else{ |
43 | playerContent.addEventListener('touchstart',playerControlShow) |
44 | } |
45 | |
46 | //hidden player control |
47 | if(!isSupportTouch){ |
48 | playerContent.addEventListener('mouseleave',playerControlHidden) |
49 | }else{ |
50 | playerContent.addEventListener('touchend',playerControlHidden) |
51 | } |
52 | |
53 | function vPlayerPlay() { |
54 | progressBar() |
55 | playerContent.play() |
56 | play.style.display = 'none'; |
57 | pause.style.display = 'block'; |
58 | } |
59 | |
60 | function vPlayerPause() { |
61 | if(_playTime){ |
62 | clearInterval(_playTime) |
63 | } |
64 | playerContent.pause() |
65 | pause.style.display = 'none'; |
66 | play.style.display = 'block'; |
67 | } |
68 | |
69 | function playerControlShow() { |
70 | // console.log('start') |
71 | if(_playerControlHidden){ |
72 | clearTimeout(_playerControlHidden) |
73 | } |
74 | if(!playerControl.style.opacity) return |
75 | playerControl.style.display = 'block' |
76 | playerControl.style.animation = 'fadeIn 1s' |
77 | playerControl.style.opacity = 1 |
78 | } |
79 | |
80 | function playerControlHidden() { |
81 | // console.log('hidden') |
82 | _playerControlHidden = setTimeout(function () { |
83 | playerControl.style.animation = 'fadeOut 1s' |
84 | playerControl.style.opacity = 0 |
85 | playerControl.style.display = 'none' |
86 | },3000) |
87 | } |
88 | |
89 | function progressBar() { |
90 | _playTime = setInterval(function () { |
91 | // console.log(playerContent.buffered.end(0)) |
92 | //reload progress time (加载的时间/总时间)*100 = 加载的百分比 |
93 | var reloadProgressTime = (playerContent.buffered.end(0) / playerContent.duration) * 100 |
94 | reloadProgressDOM.children[0].style.width = reloadProgressTime + '%' |
95 | |
96 | //progress time = (playerContent.currentTime/playerContent.duration)*100 |
97 | var progressTime = playerContent.currentTime |
98 | reloadProgressDOM.children[0].children[0].style.width = (progressTime / playerContent.duration) * 100 + '%' |
99 | |
100 | //progress time show |
101 | progressTimeShowDOM.innerText = secToTime(progressTime)+' | '+secToTime(playerContent.duration) |
102 | // console.log(playerContent.duration,playerContent.currentTime,playerContent.buffered.end(0)) |
103 | if(playerContent.currentTime === playerContent.duration){ |
104 | clearInterval(_playTime) |
105 | vPlayerPause() |
106 | reloadProgressDOM.children[0].children[0].style.width = 0 |
107 | } |
108 | },1) |
109 | } |
110 | |
111 | //volume drag control |
112 | playerContent.volume = 0.5 |
113 | dragMaskDOM.style.width = '50%' |
114 | dragBarDOM.style.left = '50%' |
115 | |
116 | if(!isSupportTouch) { |
117 | dragBarDOM.onmousedown = function (event) { |
118 | drag(event, this) |
119 | } |
120 | }else { |
121 | dragBarDOM.ontouchstart = function (event) { |
122 | console.log(event) |
123 | drag(event, this) |
124 | } |
125 | } |
126 | |
127 | /** |
128 | * drag element |
129 | * @param event event |
130 | * @param ele element |
131 | */ |
132 | function drag(event,ele) { |
133 | var event = event || window.event; |
134 | var lengthX |
135 | if(!isSupportTouch){ |
136 | lengthX = event.clientX - ele.offsetLeft; |
137 | }else { |
138 | lengthX = event.touches[0].clientX - ele.offsetLeft; |
139 | } |
140 | |
141 | if(!isSupportTouch) { |
142 | document.onmousemove = function (event) { |
143 | dragMove(event,ele,lengthX) |
144 | } |
145 | }else{ |
146 | document.ontouchmove = function (event) { |
147 | dragMove(event,ele,lengthX) |
148 | } |
149 | } |
150 | } |
151 | |
152 | /** |
153 | * drag move volume: volume+ volume- |
154 | * @param event event |
155 | * @param ele element |
156 | * @param x lengthX:clientX |
157 | */ |
158 | function dragMove(event,ele,x) { |
159 | var event = event || window.event; |
160 | var barleft |
161 | if(!isSupportTouch) { |
162 | barleft = event.clientX - x; |
163 | }else { |
164 | barleft = event.touches[0].clientX - x; |
165 | } |
166 | if(barleft < 0) |
167 | barleft = 0; |
168 | else if(barleft > vPlayVolumeDOM.offsetWidth - dragBarDOM.offsetWidth) |
169 | barleft = vPlayVolumeDOM.offsetWidth - dragBarDOM.offsetWidth; |
170 | dragMaskDOM.style.width = barleft +'px' ; |
171 | ele.style.left = barleft + 'px'; |
172 | var volumeValue = barleft/(vPlayVolumeDOM.offsetWidth-dragBarDOM.offsetWidth) |
173 | // console.log(volumeValue) |
174 | playerContent.volume = parseFloat(volumeValue) |
175 | //防止选择内容--当拖动鼠标过快时候,弹起鼠标,bar也会移动,修复bug |
176 | window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty(); |
177 | } |
178 | |
179 | document.onmouseup = function(){ |
180 | document.onmousemove = null; //移除事件 |
181 | } |
182 | document.ontouchend = function () { |
183 | document.ontouchstart = null |
184 | document.ontouchmove = null |
185 | } |
186 | |
187 | volumeIconDOM.onclick = function () { |
188 | if(playerContent.volume != 0){ |
189 | playerContent.volume = 0 |
190 | dragMaskDOM.style.width = '0' |
191 | dragBarDOM.style.left = '0' |
192 | }else{ |
193 | playerContent.volume = 0.5 |
194 | dragMaskDOM.style.width = '50%' |
195 | dragBarDOM.style.left = '50%' |
196 | } |
197 | } |
198 | |
199 | //video fullscreen |
200 | fullscreen.onclick = function () { |
201 | if(playerContent.webkitRequestFullScreen){ |
202 | playerContent.webkitRequestFullScreen() |
203 | }else if(playerContent.mozRequestFullScreen){ |
204 | playerContent.mozRequestFullScreen() |
205 | }else { |
206 | playerContent.requestFullscreen() |
207 | } |
208 | } |
209 | console.log('networkState',playerContent.networkState) |
210 | |
211 | /** |
212 | * format second to hh:mm:ss |
213 | * @param second second |
214 | * @returns {*} format time to 00:00:00 |
215 | */ |
216 | var secToTime = function (second) { |
217 | var t |
218 | if(second > -1){ |
219 | var hour = Math.floor(second/3600) |
220 | var min = Math.floor(second/60) % 60 |
221 | var sec = second % 60 |
222 | if(hour < 10) { |
223 | t = '0'+ hour + ":" |
224 | } else { |
225 | t = hour + ":" |
226 | } |
227 | if(min < 10){t += "0"} |
228 | t += min + ":" |
229 | if(sec < 10){t += "0"} |
230 | t += sec.toFixed(0) |
231 | } |
232 | return t |
233 | } |
234 | } |
整个项目很简单,希望能够帮助到准备进入video事件以及视频处理的同学,此项目已经上传到github,如果你喜欢,请记得给我加星哦!
github:https://github.com/king2088/html5-vPlayer