前置知识点:
HTML <video>
元素用于在HTML或者XHTML文档中嵌入媒体播放器,用于支持文档内的视频播放。
-
currentTime
:读取CurentTime
返回一个双精度浮点值,指示以秒为单位的媒体的当前播放位置。如果video尚未开始播放,则会在开始播放后返回偏移量。通过CurentTime
将当前播放位置设置为给定时间,会在加载媒体时将媒体查找到该位置(从指定的位置开始播放)。 -
duration
(只读):一个双精度浮点值,它指示媒体的持续时间(总长度),以秒为单位,在媒体的时间线上。 -
volume
:音量 -
playbackrate
:播放速度 -
play()
:播放视频 -
pause()
:暂停视频
更多知识点:===》 https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/video
实现思路:
- 实现播放暂停功能
// 获取 video 和 toggle
const player = document.querySelector('.player')
const video = player.querySelector('.viewer')
const toggle = player.querySelector('.toggle')
当 video 标签和 控制按钮被点击时,切换播放状态
video.addEventListener('click', togglePlay)
toggle.addEventListener('click', togglePlay)
判断 video 的状态,如果当前是暂停,则调用 video 的 play。
// 切换播放状态
function togglePlay() {
const method = video.paused ? 'play' : 'pause'
video[method]()
}
- 切换播放状态图标,对 video 进行播放状态的监听
video.addEventListener('play', updatedToggle)
video.addEventListener('pause', updatedToggle)
// 切换播放状态图标
function updatedToggle() {
const icon = video.paused ? '►' : '❚ ❚'
toggle.textContent = icon
}
- 实现前进后退功能
获取页面上的两个前进后退按钮,为其添加 click
事件。
const skipButtons = player.querySelectorAll('[data-skip]')
skipButtons.forEach(skipButton => skipButton.addEventListener('click', skip))
获取 skip 按钮上的自定义属性,改变 video 的 currentTime
function skip() {
video.currentTime += parseFloat(this.dataset.skip)
}
- 改变播放速度和音量
获取页面上控制音量和播放速度的两个按钮,为其添加 change
事件和 mousemove
事件,实现点击或拖动改变的效果。
const ranges = player.querySelectorAll('.player__slider')
ranges.forEach(range => range.addEventListener('change', handleRangeUpdate))
ranges.forEach(range => range.addEventListener('mousemove', handleRangeUpdate))
// 改变播放速度和音量
function handleRangeUpdate() {
video[this.name] = this.value
}
- 实现播放进度条的颜色移动
const progressBar = player.querySelector('.progress__filled')
监听 video
标签的 timeupdate
video.addEventListener('timeupdate', handleProgress)
用当前播放的时间 video.currentTime
除以视频的总时长 video.duration
// 改变进度条
function handleProgress() {
const percent = (video.currentTime / video.duration) * 100;
progressBar.style.flexBasis = `${percent}%`;
}
- 拖动进度条,视频切换到对应的播放时间
const progress = player.querySelector('.progress')
需要一个变量,控制切换是否生效
let mousedown = false
监听 progress 的 click
,mousemove
,mousedown
,mouseup
四个事件
let mousedown = false
progress.addEventListener('click', scrub)
progress.addEventListener('mousemove', (e) => mousedown && scrub(e))
progress.addEventListener('mousedown', () => mousedown = true)
progress.addEventListener('mouseup', () => mousedown = false)
// 点击切换播放进度
function scrub(e) {
const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration
video.currentTime = scrubTime
}
完整代码:
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HTML Video Player</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="player">
<div class="top" id="barrageList">
<video class="player__video viewer" src="652333414.mp4"></video>
</div>
<div class="player__controls">
<div class="progress">
<div class="progress__filled"></div>
</div>
<button class="player__button toggle" title="Toggle Play">►</button>
<input type="range" name="volume" class="player__slider" min="0" max="1" step="0.05" value="1">
<input type="range" name="playbackRate" class="player__slider" min="0.5" max="2" step="0.1" value="1">
<button data-skip="-10" class="player__button">« 10s</button>
<button data-skip="25" class="player__button">25s »</button>
</div>
</div>
<script src="./scripts-my.js"></script>
</body>
</html>
CSS
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
margin: 0;
padding: 0;
display: flex;
background: #7A419B;
min-height: 100vh;
background: linear-gradient(135deg, #7c1599 0%,#921099 48%,#7e4ae8 100%);
background-size: cover;
align-items: center;
justify-content: center;
}
.player {
max-width: 750px;
border: 5px solid rgba(0,0,0,0.2);
box-shadow: 0 0 20px rgba(0,0,0,0.2);
position: relative;
font-size: 0;
overflow: hidden;
}
/* This css is only applied when fullscreen is active. */
.player:fullscreen {
max-width: none;
width: 100%;
}
.player:-webkit-full-screen {
max-width: none;
width: 100%;
}
.player__video {
width: 100%;
}
.player__button {
background: none;
border: 0;
line-height: 1;
color: white;
text-align: center;
outline: 0;
padding: 0;
cursor: pointer;
max-width: 50px;
}
.player__button:focus {
border-color: #ffc600;
}
.player__slider {
width: 10px;
height: 30px;
}
.player__controls {
display: flex;
position: absolute;
bottom: 0;
width: 100%;
transform: translateY(100%) translateY(-5px);
transition: all .3s;
flex-wrap: wrap;
background: rgba(0,0,0,0.1);
}
.player:hover .player__controls {
transform: translateY(0);
}
.player:hover .progress {
height: 15px;
}
.player__controls > * {
flex: 1;
}
.progress {
flex: 10;
position: relative;
display: flex;
flex-basis: 100%;
height: 5px;
transition: height 0.3s;
background: rgba(0,0,0,0.5);
cursor: ew-resize;
}
.progress__filled {
width: 50%;
background: #ffc600;
flex: 0;
flex-basis: 0%;
}
/* unholy css to style input type="range" */
input[type=range] {
-webkit-appearance: none;
background: transparent;
width: 100%;
margin: 0 5px;
}
input[type=range]:focus {
outline: none;
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 8.4px;
cursor: pointer;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0 0 1px rgba(13, 13, 13, 0);
background: rgba(255,255,255,0.8);
border-radius: 1.3px;
border: 0.2px solid rgba(1, 1, 1, 0);
}
input[type=range]::-webkit-slider-thumb {
height: 15px;
width: 15px;
border-radius: 50px;
background: #ffc600;
cursor: pointer;
-webkit-appearance: none;
margin-top: -3.5px;
box-shadow:0 0 2px rgba(0,0,0,0.2);
}
input[type=range]:focus::-webkit-slider-runnable-track {
background: #bada55;
}
input[type=range]::-moz-range-track {
width: 100%;
height: 8.4px;
cursor: pointer;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0 0 1px rgba(13, 13, 13, 0);
background: #ffffff;
border-radius: 1.3px;
border: 0.2px solid rgba(1, 1, 1, 0);
}
input[type=range]::-moz-range-thumb {
box-shadow: 0 0 0 rgba(0, 0, 0, 0), 0 0 0 rgba(13, 13, 13, 0);
height: 15px;
width: 15px;
border-radius: 50px;
background: #ffc600;
cursor: pointer;
}
.top video {
width: 100%;
}
.bot {
display: flex;
}
.bot .barrage-color {
width: 80px;
}
.bot .barrage-input {
width: 240px;
margin-left: -15px;
}
.bot .barrage-input input {
width: 100%;
height: 25px;
font-size: 16px;
}
#textColor {
width: 50px;
height: 25px;
border: none;
}
.bot .barrage-btn {
flex: 1;
text-align: center;
background: cyan;
line-height: 25px;
cursor: pointer;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
}
.msg {
transform: translateX(-100px);
position: absolute;
z-index: 999;
font-weight: 800;
animation: l-2-r 6s linear;
}
/* 从左到右的移动动画 */
@keyframes l-2-r {
from {
transform: translateX(500px);
}
to {
transform: translateX(-100px);
}
}
JavaScript
// 首先实现播放暂停功能
const player = document.querySelector('.player')
const video = player.querySelector('.viewer')
const toggle = player.querySelector('.toggle')
const skipButtons = player.querySelectorAll('[data-skip]')
const volumeInput = player.querySelector('input[name="volume"]')
const ranges = player.querySelectorAll('.player__slider')
const progress = player.querySelector('.progress')
const progressBar = player.querySelector('.progress__filled')
const barrageList = document.getElementById('barrageList')
video.addEventListener('click', togglePlay)
toggle.addEventListener('click', togglePlay)
video.addEventListener('play', updatedToggle)
video.addEventListener('pause', updatedToggle)
video.addEventListener('timeupdate', handleProgress)
skipButtons.forEach(skipButton => skipButton.addEventListener('click', skip))
ranges.forEach(range => range.addEventListener('change', handleRangeUpdate))
ranges.forEach(range => range.addEventListener('mousemove', handleRangeUpdate))
let mousedown = false
progress.addEventListener('click', scrub)
progress.addEventListener('mousemove', (e) => mousedown && scrub(e))
progress.addEventListener('mousedown', () => mousedown = true)
progress.addEventListener('mouseup', () => mousedown = false)
// 切换播放状态
function togglePlay() {
const method = video.paused ? 'play' : 'pause'
video[method]()
}
// 切换播放状态图标
function updatedToggle() {
const icon = video.paused ? '►' : '❚ ❚'
toggle.textContent = icon
}
// 前进后退功能
function skip() {
video.currentTime += parseFloat(this.dataset.skip)
}
// 改变播放速度和音量
function handleRangeUpdate() {
video[this.name] = this.value
}
// 改变进度条
function handleProgress() {
const percent = (video.currentTime / video.duration) * 100;
progressBar.style.flexBasis = `${percent}%`;
}
// 点击切换播放进度
function scrub(e) {
const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration
video.currentTime = scrubTime
}