Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b2f3fc9

Browse files
Merge pull request #17 from doboianh/videoplayer-doboianh-branch
Video Player Customize
2 parents 1d61d68 + 1a51ce4 commit b2f3fc9

File tree

6 files changed

+697
-0
lines changed

6 files changed

+697
-0
lines changed

‎Video-Doboianh/Screenshot (301).png

797 KB
Loading[フレーム]

‎Video-Doboianh/index.html

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
7+
<title>Custom video player</title>
8+
<link rel="stylesheet" href="style.css" type="text/css">
9+
</head>
10+
<body>
11+
<div class="container">
12+
<div class="video-container" id="video-container">
13+
<div class="playback-animation" id="playback-animation">
14+
<svg class="playback-icons">
15+
<use class="hidden" href="#play-icon"></use>
16+
<use href="#pause"></use>
17+
</svg>
18+
</div>
19+
20+
<video controls class="video" id="video" preload="metadata" poster="poster.jpg">
21+
<source src="video.mp4" type="video/mp4"></source>
22+
</video>
23+
24+
<div class="video-controls hidden" id="video-controls">
25+
<div class="video-progress">
26+
<progress id="progress-bar" value="0" min="0"></progress>
27+
<input class="seek" id="seek" value="0" min="0" type="range" step="1">
28+
<div class="seek-tooltip" id="seek-tooltip">00:00</div>
29+
</div>
30+
31+
<div class="bottom-controls">
32+
<div class="left-controls">
33+
<button data-title="Play (k)" id="play">
34+
<svg class="playback-icons">
35+
<use href="#play-icon"></use>
36+
<use class="hidden" href="#pause"></use>
37+
</svg>
38+
</button>
39+
40+
<div class="volume-controls">
41+
<button data-title="Mute (m)" class="volume-button" id="volume-button">
42+
<svg>
43+
<use class="hidden" href="#volume-mute"></use>
44+
<use class="hidden" href="#volume-low"></use>
45+
<use href="#volume-high"></use>
46+
</svg>
47+
</button>
48+
49+
<input class="volume" id="volume" value="1"
50+
data-mute="0.5" type="range" max="1" min="0" step="0.01">
51+
</div>
52+
53+
<div class="time">
54+
<time id="time-elapsed">00:00</time>
55+
<span> / </span>
56+
<time id="duration">00:00</time>
57+
</div>
58+
</div>
59+
60+
<div class="right-controls">
61+
<button data-title="Full screen (f)" class="fullscreen-button" id="fullscreen-button">
62+
<svg>
63+
<use href="#fullscreen"></use>
64+
<use href="#fullscreen-exit" class="hidden"></use>
65+
</svg>
66+
</button>
67+
</div>
68+
</div>
69+
</div>
70+
</div>
71+
</div>
72+
73+
<svg style="display: none">
74+
<defs>
75+
<symbol id="pause" viewBox="0 0 24 24">
76+
<path d="M14.016 5.016h3.984v13.969h-3.984v-13.969zM6 18.984v-13.969h3.984v13.969h-3.984z"></path>
77+
</symbol>
78+
79+
<symbol id="play-icon" viewBox="0 0 24 24">
80+
<path d="M8.016 5.016l10.969 6.984-10.969 6.984v-13.969z"></path>
81+
</symbol>
82+
83+
<symbol id="volume-high" viewBox="0 0 24 24">
84+
<path d="M14.016 3.234q3.047 0.656 5.016 3.117t1.969 5.648-1.969 5.648-5.016 3.117v-2.063q2.203-0.656 3.586-2.484t1.383-4.219-1.383-4.219-3.586-2.484v-2.063zM16.5 12q0 2.813-2.484 4.031v-8.063q1.031 0.516 1.758 1.688t0.727 2.344zM3 9h3.984l5.016-5.016v16.031l-5.016-5.016h-3.984v-6z"></path>
85+
</symbol>
86+
87+
<symbol id="volume-low" viewBox="0 0 24 24">
88+
<path d="M5.016 9h3.984l5.016-5.016v16.031l-5.016-5.016h-3.984v-6zM18.516 12q0 2.766-2.531 4.031v-8.063q1.031 0.516 1.781 1.711t0.75 2.32z"></path>
89+
</symbol>
90+
91+
<symbol id="volume-mute" viewBox="0 0 24 24">
92+
<path d="M12 3.984v4.219l-2.109-2.109zM4.266 3l16.734 16.734-1.266 1.266-2.063-2.063q-1.547 1.313-3.656 1.828v-2.063q1.172-0.328 2.25-1.172l-4.266-4.266v6.75l-5.016-5.016h-3.984v-6h4.734l-4.734-4.734zM18.984 12q0-2.391-1.383-4.219t-3.586-2.484v-2.063q3.047 0.656 5.016 3.117t1.969 5.648q0 2.203-1.031 4.172l-1.5-1.547q0.516-1.266 0.516-2.625zM16.5 12q0 0.422-0.047 0.609l-2.438-2.438v-2.203q1.031 0.516 1.758 1.688t0.727 2.344z"></path>
93+
</symbol>
94+
95+
<symbol id="fullscreen" viewBox="0 0 24 24">
96+
<path d="M14.016 5.016h4.969v4.969h-1.969v-3h-3v-1.969zM17.016 17.016v-3h1.969v4.969h-4.969v-1.969h3zM5.016 9.984v-4.969h4.969v1.969h-3v3h-1.969zM6.984 14.016v3h3v1.969h-4.969v-4.969h1.969z"></path>
97+
</symbol>
98+
99+
<symbol id="fullscreen-exit" viewBox="0 0 24 24">
100+
<path d="M15.984 8.016h3v1.969h-4.969v-4.969h1.969v3zM14.016 18.984v-4.969h4.969v1.969h-3v3h-1.969zM8.016 8.016v-3h1.969v4.969h-4.969v-1.969h3zM5.016 15.984v-1.969h4.969v4.969h-1.969v-3h-3z"></path>
101+
</symbol>
102+
</defs>
103+
</svg>
104+
105+
<script src="index.js"></script>
106+
107+
</body>
108+
</html>

‎Video-Doboianh/index.js

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
// Select elements here
2+
const video = document.getElementById('video');
3+
const videoControls = document.getElementById('video-controls');
4+
const playButton = document.getElementById('play');
5+
const playbackIcons = document.querySelectorAll('.playback-icons use');
6+
const timeElapsed = document.getElementById('time-elapsed');
7+
const duration = document.getElementById('duration');
8+
const progressBar = document.getElementById('progress-bar');
9+
const seek = document.getElementById('seek');
10+
const seekTooltip = document.getElementById('seek-tooltip');
11+
const volumeButton = document.getElementById('volume-button');
12+
const volumeIcons = document.querySelectorAll('.volume-button use');
13+
const volumeMute = document.querySelector('use[href="#volume-mute"]');
14+
const volumeLow = document.querySelector('use[href="#volume-low"]');
15+
const volumeHigh = document.querySelector('use[href="#volume-high"]');
16+
const volume = document.getElementById('volume');
17+
const playbackAnimation = document.getElementById('playback-animation');
18+
const fullscreenButton = document.getElementById('fullscreen-button');
19+
const videoContainer = document.getElementById('video-container');
20+
const fullscreenIcons = fullscreenButton.querySelectorAll('use');
21+
22+
const videoWorks = !!document.createElement('video').canPlayType;
23+
if (videoWorks) {
24+
video.controls = false;
25+
videoControls.classList.remove('hidden');
26+
}
27+
28+
// Add functions here
29+
30+
// togglePlay toggles the playback state of the video.
31+
// If the video playback is paused or ended, the video is played
32+
// otherwise, the video is paused
33+
function togglePlay() {
34+
if (video.paused || video.ended) {
35+
video.play();
36+
} else {
37+
video.pause();
38+
}
39+
}
40+
41+
// updatePlayButton updates the playback icon and tooltip
42+
// depending on the playback state
43+
function updatePlayButton() {
44+
playbackIcons.forEach((icon) => icon.classList.toggle('hidden'));
45+
46+
if (video.paused) {
47+
playButton.setAttribute('data-title', 'Play (k)');
48+
} else {
49+
playButton.setAttribute('data-title', 'Pause (k)');
50+
}
51+
}
52+
53+
// formatTime takes a time length in seconds and returns the time in
54+
// minutes and seconds
55+
function formatTime(timeInSeconds) {
56+
const result = new Date(timeInSeconds * 1000).toISOString().substr(11, 8);
57+
58+
return {
59+
minutes: result.substr(3, 2),
60+
seconds: result.substr(6, 2),
61+
};
62+
}
63+
64+
// initializeVideo sets the video duration, and maximum value of the
65+
// progressBar
66+
function initializeVideo() {
67+
const videoDuration = Math.round(video.duration);
68+
seek.setAttribute('max', videoDuration);
69+
progressBar.setAttribute('max', videoDuration);
70+
const time = formatTime(videoDuration);
71+
duration.innerText = `${time.minutes}:${time.seconds}`;
72+
duration.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`);
73+
}
74+
75+
// updateTimeElapsed indicates how far through the video
76+
// the current playback is by updating the timeElapsed element
77+
function updateTimeElapsed() {
78+
const time = formatTime(Math.round(video.currentTime));
79+
timeElapsed.innerText = `${time.minutes}:${time.seconds}`;
80+
timeElapsed.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`);
81+
}
82+
83+
// updateProgress indicates how far through the video
84+
// the current playback is by updating the progress bar
85+
function updateProgress() {
86+
seek.value = Math.floor(video.currentTime);
87+
progressBar.value = Math.floor(video.currentTime);
88+
}
89+
90+
// updateSeekTooltip uses the position of the mouse on the progress bar to
91+
// roughly work out what point in the video the user will skip to if
92+
// the progress bar is clicked at that point
93+
function updateSeekTooltip(event) {
94+
const skipTo = Math.round(
95+
(event.offsetX / event.target.clientWidth) *
96+
parseInt(event.target.getAttribute('max'), 10)
97+
);
98+
seek.setAttribute('data-seek', skipTo);
99+
const t = formatTime(skipTo);
100+
seekTooltip.textContent = `${t.minutes}:${t.seconds}`;
101+
const rect = video.getBoundingClientRect();
102+
seekTooltip.style.left = `${event.pageX - rect.left}px`;
103+
}
104+
105+
// skipAhead jumps to a different point in the video when the progress bar
106+
// is clicked
107+
function skipAhead(event) {
108+
const skipTo = event.target.dataset.seek
109+
? event.target.dataset.seek
110+
: event.target.value;
111+
video.currentTime = skipTo;
112+
progressBar.value = skipTo;
113+
seek.value = skipTo;
114+
}
115+
116+
// updateVolume updates the video's volume
117+
// and disables the muted state if active
118+
function updateVolume() {
119+
if (video.muted) {
120+
video.muted = false;
121+
}
122+
123+
video.volume = volume.value;
124+
}
125+
126+
// updateVolumeIcon updates the volume icon so that it correctly reflects
127+
// the volume of the video
128+
function updateVolumeIcon() {
129+
volumeIcons.forEach((icon) => {
130+
icon.classList.add('hidden');
131+
});
132+
133+
volumeButton.setAttribute('data-title', 'Mute (m)');
134+
135+
if (video.muted || video.volume === 0) {
136+
volumeMute.classList.remove('hidden');
137+
volumeButton.setAttribute('data-title', 'Unmute (m)');
138+
} else if (video.volume > 0 && video.volume <= 0.5) {
139+
volumeLow.classList.remove('hidden');
140+
} else {
141+
volumeHigh.classList.remove('hidden');
142+
}
143+
}
144+
145+
// toggleMute mutes or unmutes the video when executed
146+
// When the video is unmuted, the volume is returned to the value
147+
// it was set to before the video was muted
148+
function toggleMute() {
149+
video.muted = !video.muted;
150+
151+
if (video.muted) {
152+
volume.setAttribute('data-volume', volume.value);
153+
volume.value = 0;
154+
} else {
155+
volume.value = volume.dataset.volume;
156+
}
157+
}
158+
159+
// animatePlayback displays an animation when
160+
// the video is played or paused
161+
function animatePlayback() {
162+
playbackAnimation.animate(
163+
[
164+
{
165+
opacity: 1,
166+
transform: 'scale(1)',
167+
},
168+
{
169+
opacity: 0,
170+
transform: 'scale(1.3)',
171+
},
172+
],
173+
{
174+
duration: 500,
175+
}
176+
);
177+
}
178+
179+
// toggleFullScreen toggles the full screen state of the video
180+
// If the browser is currently in fullscreen mode,
181+
// then it should exit and vice versa.
182+
function toggleFullScreen() {
183+
if (document.fullscreenElement) {
184+
document.exitFullscreen();
185+
} else if (document.webkitFullscreenElement) {
186+
// Need this to support Safari
187+
document.webkitExitFullscreen();
188+
} else if (videoContainer.webkitRequestFullscreen) {
189+
// Need this to support Safari
190+
videoContainer.webkitRequestFullscreen();
191+
} else {
192+
videoContainer.requestFullscreen();
193+
}
194+
}
195+
196+
// updateFullscreenButton changes the icon of the full screen button
197+
// and tooltip to reflect the current full screen state of the video
198+
function updateFullscreenButton() {
199+
fullscreenIcons.forEach((icon) => icon.classList.toggle('hidden'));
200+
201+
if (document.fullscreenElement) {
202+
fullscreenButton.setAttribute('data-title', 'Exit full screen (f)');
203+
} else {
204+
fullscreenButton.setAttribute('data-title', 'Full screen (f)');
205+
}
206+
}
207+
208+
// togglePip toggles Picture-in-Picture mode on the video
209+
async function togglePip() {
210+
try {
211+
if (video !== document.pictureInPictureElement) {
212+
pipButton.disabled = true;
213+
await video.requestPictureInPicture();
214+
} else {
215+
await document.exitPictureInPicture();
216+
}
217+
} catch (error) {
218+
console.error(error);
219+
} finally {
220+
pipButton.disabled = false;
221+
}
222+
}
223+
224+
// hideControls hides the video controls when not in use
225+
// if the video is paused, the controls must remain visible
226+
function hideControls() {
227+
if (video.paused) {
228+
return;
229+
}
230+
231+
videoControls.classList.add('hide');
232+
}
233+
234+
// showControls displays the video controls
235+
function showControls() {
236+
videoControls.classList.remove('hide');
237+
}
238+
239+
// keyboardShortcuts executes the relevant functions for
240+
// each supported shortcut key
241+
function keyboardShortcuts(event) {
242+
const { key } = event;
243+
switch (key) {
244+
case 'k':
245+
togglePlay();
246+
animatePlayback();
247+
if (video.paused) {
248+
showControls();
249+
} else {
250+
setTimeout(() => {
251+
hideControls();
252+
}, 2000);
253+
}
254+
break;
255+
case 'm':
256+
toggleMute();
257+
break;
258+
case 'f':
259+
toggleFullScreen();
260+
break;
261+
}
262+
}
263+
264+
// Add eventlisteners here
265+
playButton.addEventListener('click', togglePlay);
266+
video.addEventListener('play', updatePlayButton);
267+
video.addEventListener('pause', updatePlayButton);
268+
video.addEventListener('loadedmetadata', initializeVideo);
269+
video.addEventListener('timeupdate', updateTimeElapsed);
270+
video.addEventListener('timeupdate', updateProgress);
271+
video.addEventListener('volumechange', updateVolumeIcon);
272+
video.addEventListener('click', togglePlay);
273+
video.addEventListener('click', animatePlayback);
274+
video.addEventListener('mouseenter', showControls);
275+
video.addEventListener('mouseleave', hideControls);
276+
videoControls.addEventListener('mouseenter', showControls);
277+
videoControls.addEventListener('mouseleave', hideControls);
278+
seek.addEventListener('mousemove', updateSeekTooltip);
279+
seek.addEventListener('input', skipAhead);
280+
volume.addEventListener('input', updateVolume);
281+
volumeButton.addEventListener('click', toggleMute);
282+
fullscreenButton.addEventListener('click', toggleFullScreen);
283+
videoContainer.addEventListener('fullscreenchange', updateFullscreenButton);
284+
285+
document.addEventListener('DOMContentLoaded', () => {
286+
if (!('pictureInPictureEnabled' in document)) {
287+
pipButton.classList.add('hidden');
288+
}
289+
});
290+
document.addEventListener('keyup', keyboardShortcuts);

‎Video-Doboianh/poster.jpg

106 KB
Loading[フレーム]

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /