H5支持video标签,但是默认样式难以满足日常需求,早晚都要做,就先写个demo出来方便以后使用了。
使用前需要了解的鼠标交互事件链接https://blog.csdn.net/zeping891103/article/details/70208945
关于视频播放相关的有疑问的可以结合的音乐播放器那章一起看https://www.jianshu.com/p/75d160a6cbbd
页面引用
<template>
<div class="home-page">
<mvideo :videoSrc.sync="url"></mvideo>
<Button @click="ChangeUrl">切换</Button>
</div>
</template>
import Mvideo from '@/components/videoPlay'
export default {
data() {
return {
url: '../../asset/video/video_01.mp4'
};
},
components: {
Mvideo,
},
methods: {
ChangeUrl(){
this.url = '../../asset/video/video_02.mp4'
}
},
}
组件部分--首先有一个总容器,包含video和自定义的控制条
<div class="main-container" ref="vcontainer">
<video class="video-player" preload="preload" id="video" ref="v">
<source :src="videoSrc" />
</video>
<div ref="track" style="width: 100%"></div>
<transition name="fade">
<div class="controller" v-show="isControlVisible"> </div>
</transition>
</div>
视频部分不用处理了,主要是自定义底部控制条的样式,这里就看各位八仙过海了。
组件内部用到的一些变量
data() {
return {
isControlVisible: false, //自定义控制器是否可见
isPaused: true, //是否暂停
isDragging: false, //是否拖拽进度条
currentTime: "00:00", //当前播放时间
videoDuration: 0, //视频时长
videoProgress: 0, //视频进度百分比 0-1
thumbTranslateX: 0, // 进度条滑块位置
hidingEvent: null,
};
},
首先是播放时间/视频时长+进度条
<div class="controller_btn-wrapper">
<div class="controller_btn" @click="Playing">
<img
v-show="isPaused"
class="play-icon"
src="@asset/icon/play_01.png"
/>
<img
v-show="!isPaused"
class="play-icon"
src="@asset/icon/play_02.png"
/>
</div>
视频播放进度
<div class="controller_time">
{{ currentTime }} / {{ videoDuration }}
</div>
全屏按钮
<div class="fullScreen">
<img @click="ToggleFullscreen" src="@assets/icon_02.png" alt="" />
</div>
</div>
进度条部分
<div
class="progress-container"
id="progress"
@click="handleProgressClick($event)"
@pointerup="stopDragging"
>
<div class="progress_box" :style="{ width: videoProgressPercent }">
<div
class="play_point"
:style="{ transform: 'translateX(' + thumbTranslateX + 'px)' }"
@pointerdown="startDragging($event)"
>
<img src="@assets/icon_01.png" alt="" />
</div>
</div>
</div>
进度条思路是外层div设置背景色,内部进度条width实时变化达到进度的效果
我的样式设置
.progress-container {
position: absolute;
width: 100%;
top: -2.5px;
height: 5px;
background: rgba(255, 255, 255, 0.5);
z-index: 100;
cursor: pointer;
.progress_box {
z-index: 99;
position: relative;
background: #409eff;
height: 5px;
.play_point {
transition: -webkit-transform 0.2s linear;
transition: transform 0.2s linear;
transition: transform 0.2s linear, -webkit-transform 0.2s linear;
position: absolute;
top: -7px;
left: 0;
width: 20px;
background: #fff;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
img {
width: 18px;
cursor: pointer;
}
}
}
}
mounted() {
// 一定要放在mounted定义video
const video = document.getElementById("video");
this.videoInit();
},
computed: {
//计算属性--获取进度条已播放进度0-100%
videoProgressPercent() {
return `${this.videoProgress * 100}%`;
},
},
methods() {
videoInit() {
let that = this;
let progressL = this.$refs.track.offsetWidth; // 进度条总长
// 音频或视频文件已经就绪可以开始
video.addEventListener("canplay", () => {
that.videoDuration = that.timeToString(video.duration);
});
video.addEventListener("timeupdate", () => {
// 当前播放时间
that.currentTime = that.timeToString(video.currentTime);
that.videoProgress = video.currentTime / video.duration;
that.thumbTranslateX = (that.videoProgress * progressL).toFixed(3);
});
video.addEventListener("ended", () => {
setTimeout(() => {
video.play();
}, 100);
});
},
// 秒值转字符串
timeToString(param) {
param = parseInt(param);
let hh = "",
mm = "",
ss = "";
if (param >= 0 && param < 60) {
param < 10 ? (ss = "0" + param) : (ss = param);
return "00:" + ss;
} else if (param >= 60 && param < 3600) {
mm = parseInt(param / 60);
mm < 10 ? (mm = "0" + mm) : mm;
param - parseInt(mm * 60) < 10
? (ss = "0" + String(param - parseInt(mm * 60)))
: (ss = param - parseInt(mm * 60));
return mm + ":" + ss;
}
},
},
到这步,就实现了播放暂停+播放时间进度+进度条;
接着是点击进度条控制视频时间和拖拽进度按钮实现视频跳转进度
先点击进度条来实现跳转
//点击进度条
handleProgressClick(event) {
let progressL = this.$refs.track.offsetWidth; // 进度条总长
let clickX = event.offsetX;
console.log(clickX, progressL);
let time = (clickX / progressL).toFixed(2);
this.setProgress(time);
},
setProgress(x) {
video.currentTime = video.duration * x;
},
拖拽进度条--需要给最外层容器添加@pointermove.prevent事件
<div
class="main-container"
@pointermove.prevent="handleMouseMove($event)"
ref="vcontainer"
>
</div>
handleMouseMove(event) {
this.showControlBar();
this.showCursor();
if (this.hidingEvent !== null) {
clearInterval(this.hidingEvent);
}
this.hidingEvent = setInterval(this.hideControlBar, 3000);//鼠标禁止不动3秒关闭自定义控制条
this.moveDragging(event);
},
//展示控制条
showControlBar() {
this.isControlVisible = true;
},
//关闭控制条
hideControlBar() {
const isFullscreen = document.webkitIsFullScreen || document.fullscreen;
if (isFullscreen) {
this.hideCursor();
}
this.isControlVisible = false;
},
hideCursor() {
document.body.style.cursor = "none";
},
showCursor() {
document.body.style.cursor = "default";
},
moveDragging(event) {},
//开始拖拽
startDragging(event) {
this.PauseVideo();
this.isDragging = true;
},
//停止拖拽
stopDragging() {
this.isDragging = false;
this.PlayVideo();
},
最后加上鼠标移入移出来展示和关闭控制条
<div
class="main-container"
@pointermove.prevent="handleMouseMove($event)"
@pointerleave="handleMouseLeave"
@pointerenter="handleMouseEnter"
ref="vcontainer"
>
</div>
handleMouseLeave() {
this.hideControlBar();
},
//隐藏显示控制条
handleMouseEnter() {
this.showControlBar();
},
//全屏方法
ToggleFullscreen() {
const isFullscreen = document.webkitIsFullScreen || document.fullscreen;
if (isFullscreen) {
const exitFunc =
document.exitFullscreen || document.webkitExitFullscreen;
exitFunc.call(document);
} else {
const element = this.$refs.vcontainer;
const fullscreenFunc =
element.requestFullscreen || element.webkitRequestFullScreen;
fullscreenFunc.call(element);
}
},
组件完整代码--css+html+js
<style lang="less" scoped>
.main-container {
position: relative;
.video-player {
width: 100%;
display: flex;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.8s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
.controller {
flex-direction: column;
height: 50px;
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.5);
.controller_btn-wrapper {
position: relative;
height: 50px;
display: flex;
align-items: center;
color: #fff;
padding: 0 18px;
.controller_btn {
cursor: pointer;
transition: 0.5s;
margin: 7px 20px;
.play-icon {
margin-top: 2px;
width: 36px;
height: 36px;
}
}
.fullScreen {
transition: 0.5s;
position: absolute;
right: 0;
top: 0;
width: 80px;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
img {
width: 36px;
height: 36px;
cursor: pointer;
}
}
.controller_btn:hover {
color: #409eff;
}
.controller_time {
width: 100px;
height: 100%;
line-height: 50px;
}
}
.process-container {
position: absolute;
width: 100%;
top: -2.5px;
height: 5px;
background: rgba(255, 255, 255, 0.5);
z-index: 100;
cursor: pointer;
.progress_box {
z-index: 99;
position: relative;
background: #409eff;
height: 5px;
.play_point {
transition: -webkit-transform 0.2s linear;
transition: transform 0.2s linear;
transition: transform 0.2s linear, -webkit-transform 0.2s linear;
position: absolute;
top: -7px;
left: 0;
width: 20px;
background: #fff;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
img {
width: 18px;
cursor: pointer;
}
}
}
}
}
}
</style>
<template>
<div
class="main-container"
@pointermove.prevent="handleMouseMove($event)"
@pointerleave="handleMouseLeave"
@pointerenter="handleMouseEnter"
ref="vcontainer"
>
<video class="video-player" preload="preload" id="video" ref="v">
<source :src="videoSrc" />
</video>
<div ref="track" style="width: 100%"></div>
<transition name="fade">
<div class="controller" v-show="isControlVisible">
<div class="controller_btn-wrapper">
<div class="controller_btn" @click="Playing">
<img
v-show="isPaused"
class="play-icon"
src="../../pic/icon_01.png"
/>
<img
v-show="!isPaused"
class="play-icon"
src="../../pic/icon_02.png"
/>
</div>
<div class="controller_time">
{{ currentTime }} / {{ videoDuration }}
</div>
<div class="fullScreen">
<img @click="ToggleFullscreen" src="@assets/icon_02.png" alt="" />
</div>
</div>
<div
class="process-container"
id="progress"
@click="handleProgressClick($event)"
@pointerup="stopDragging"
>
<div class="progress_box" :style="{ width: videoProgressPercent }">
<div
class="play_point"
:style="{ transform: 'translateX(' + thumbTranslateX + 'px)' }"
@pointerdown="startDragging($event)"
>
<img src="@assets/icon_01.png" alt="" />
</div>
</div>
</div>
</div>
</transition>
</div>
</template>
<script>
export default {
name: "MyVideo",
props: {
videoSrc: {
type: String,
default:
"@asset/video/video_01.mp4",
},
},
data() {
return {
isControlVisible: false,
isPaused: true,
isDragging: false,
currentTime: "00:00",
videoDuration: 0,
videoProgress: 0,
thumbTranslateX: 0, // 进度条滑块位置
hidingEvent: null,
};
},
created() {},
mounted() {
const video = document.getElementById("video");
this.videoInit();
},
computed: {
videoProgressPercent() {
return `${this.videoProgress * 100}%`;
},
getUrl() {
return this.videoSrc;
},
},
watch: {
getUrl(curval, oldval) {
console.log(`最新值${curval}--旧值${oldval}`);
this.PlayNew(curval);
},
},
methods: {
videoInit() {
let that = this;
let progressL = this.$refs.track.offsetWidth; // 进度条总长
// 音频或视频文件已经就绪可以开始
video.addEventListener("canplay", () => {
that.videoDuration = that.timeToString(video.duration);
});
video.addEventListener("timeupdate", () => {
// 当前播放时间
that.currentTime = that.timeToString(video.currentTime);
that.videoProgress = video.currentTime / video.duration;
that.thumbTranslateX = (that.videoProgress * progressL).toFixed(3);
});
video.addEventListener("ended", () => {
setTimeout(() => {
video.play();
}, 100);
});
},
//url地址变化后重新加载视频
PlayNew(val) {
this.isPaused = false
video.src = this.videoSrc
setTimeout(() => {
this.totalTime = "00:00";
video.play();
}, 100);
},
Playing() {
if (video.paused) {
this.PlayVideo();
} else {
this.PauseVideo();
}
},
handleEnd() {
this.PauseVideo();
},
//播放
PlayVideo() {
this.isPaused = false;
video.play();
},
//暂停
PauseVideo() {
this.isPaused = true;
video.pause();
},
// 秒值转字符串
timeToString(param) {
param = parseInt(param);
let hh = "",
mm = "",
ss = "";
if (param >= 0 && param < 60) {
param < 10 ? (ss = "0" + param) : (ss = param);
return "00:" + ss;
} else if (param >= 60 && param < 3600) {
mm = parseInt(param / 60);
mm < 10 ? (mm = "0" + mm) : mm;
param - parseInt(mm * 60) < 10
? (ss = "0" + String(param - parseInt(mm * 60)))
: (ss = param - parseInt(mm * 60));
return mm + ":" + ss;
}
},
//隐藏显示控制条
handleMouseEnter() {
this.showControlBar();
},
handleMouseMove(event) {
this.showControlBar();
this.showCursor();
if (this.hidingEvent !== null) {
clearInterval(this.hidingEvent);
}
this.hidingEvent = setInterval(this.hideControlBar, 3000);
this.moveDragging(event);
},
handleMouseLeave() {
this.hideControlBar();
},
//展示控制条
showControlBar() {
this.isControlVisible = true;
},
//关闭控制条
hideControlBar() {
const isFullscreen = document.webkitIsFullScreen || document.fullscreen;
if (isFullscreen) {
this.hideCursor();
}
this.isControlVisible = false;
},
hideCursor() {
document.body.style.cursor = "none";
},
showCursor() {
document.body.style.cursor = "default";
},
//点击进度条
handleProgressClick(event) {
let progressL = this.$refs.track.offsetWidth; // 进度条总长
let clickX = event.offsetX;
console.log(clickX, progressL);
let time = (clickX / progressL).toFixed(2);
this.setProgress(time);
},
setProgress(x) {
video.currentTime = video.duration * x;
},
//拖拽进度条
startDragging(event) {
this.PauseVideo();
this.isDragging = true;
},
moveDragging(event) {},
stopDragging() {
this.isDragging = false;
this.PlayVideo();
},
ToggleFullscreen() {
const isFullscreen = document.webkitIsFullScreen || document.fullscreen;
if (isFullscreen) {
const exitFunc =
document.exitFullscreen || document.webkitExitFullscreen;
exitFunc.call(document);
} else {
const element = this.$refs.vcontainer;
const fullscreenFunc =
element.requestFullscreen || element.webkitRequestFullScreen;
fullscreenFunc.call(element);
}
},
},
};
</script>