vue Demo实现
本篇主要介绍基于vue2的模板定义,vue2模板其实也比较偏面相对象。实际开发过程中,当前后端分离时,我们在接到需求的时候,往往可以先于服务端进行开发,提前实现模板页面的代码,以及提前定义我们自己需要的属性和方法。等到我们进行前后端联调的时候,无非就是请求接口然后把接口里面的数据赋值给我们提前定义好的属性即可。 本篇文章主要是demo的html+css实现,以及基本的数据结构和方法定义。
模板及属性方法定义
模板:
html
<div class="video-wrapper">
<video
:src="videoSrc"
id="video"
class="player"
style="object-fit:fill"
webkit-playsinline="true"
playsinline="true"
x5-playsinline
preload="auto"
:poster="poster"
x-webkit-airplay="true"
ref="video"
></video>
<div
class="fz-player-controls-panel-wrapper"
id="playerPanel"
@click="ShowControl"
>
<a
href="javascript:;"
class="play-btn-wrapper"
id="playBtn"
v-if="showStartPlay"
@click.stop="PlayVideo"
>
<img src="https://cdn.cn/common/play-icon.png">
</a>
<div
class="fz-player-controls-panel"
:style="{opacity:showPannel?'1':'0'}"
id="panelControls"
>
<div class="progress-wrapper">
<a
href="javascript:;"
class="mini-play-btn-wrapper"
id="miniPlayBtn"
@click="PlayVideo"
>
<img
src="https://cdn.cn/common/play-mini.png?a=1"
id='playBtnIcon'
v-if="video.starting==false"
/>
<img
src="https://cdn.cn/common/stop-mini.png?a=1"
id='playBtnIcon'
v-else
/>
</a>
<div
class="current-time"
id="currentTime"
>{{video.currentTimeString}}</div>
<div
class="progress"
id="progress"
ref="progress"
@click="Seek($event)"
>
<div class="progress-line"></div>
<div
class="progress-current-line"
id="progressCurrentLine"
ref="progressCurrentLine"
></div>
<div
class="current-indicator-wrapper"
id="indicator"
ref="indicator"
@touchstart.stop="TouchStart($event)"
@touchmove.stop="TouchMove($event)"
@touchend.stop="TouchEnd($event)"
>
<div class="current-indicator"></div>
<div class="current-indicator-inner"></div>
</div>
</div>
<div
class="duration-time"
id="durationTime"
>{{video.durationString}}</div>
<a
href="javascript:;"
class="full-btn-wrapper"
id="fullBtn"
@click="SetFullScreen"
>
<img src="https://cdn.cn/common/full-icon.png" />
</a>
</div>
</div>
</div>
</div>Ï
注意
- 正常的video在手机端播放的时候都是默认全屏播放,因此我们需要控制视频初始内联播放,因此需要设置一下属性:
webkit-playsinline="true"
playsinline="true"
x5-playsinline
这里同时设置了3个playsinline主要是兼容不同的设备
webkit-playsinline="true":这是一个非标准的Apple属性,用于在iOS Safari浏览器上控制视频的内联播放。当该属性设置为true时,视频将在页面内播放,而不是全屏播放。
playsinline="true":这是HTML5的标准属性,与webkit-playsinline的功能相同。它也是用来控制视频是否在页面内播放,而不是全屏播放。这个属性在非Safari浏览器(例如Chrome和Firefox)上更有用。
x5-playsinline:这是由腾讯X5内核浏览器(例如QQ浏览器,微信内置浏览器)提供的属性,用于控制视频的内联播放。
2.#playerPanel 节点为control-mask节点是父元素,绑定了click方法,因此其内部的子元素的一些绑定的可能出现冲突的事件都需要阻止冒泡,故而我们绑定时候这样写就可以了: @click.stop
3.我们知道控制一个元素隐藏有很多种方式,有display设置为none和block,有visibility设置为visible和hidden,还有opacity设置为0和1。这里采用opacity主要是为了实现淡入淡出的效果,同时能够在初始化的时候拿到子元素渲染后的宽高等信息(当初始display为none的时候实际上是拿不到offsetLeft和offsetWidth等信息的)
样式:
less
.video-wrapper {
width: 100%;
position: relative;
width: 7.5rem;
height: 4.2168rem;
.player {
width: 100%;
height: 100%;
}
.poster {
width: 100%;
height: 100%;
position: absolute;
z-index: 1;
top: 0;
left: 0;
img {
width: 100%;
height: 100%;
}
}
.loading-icon {
width: 0.72rem;
height: 0.72rem;
position: absolute;
z-index: 1;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
margin: 0 auto;
}
.fz-player-controls-panel-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
.play-btn-wrapper {
width: 1.2rem;
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 0;
right: 0;
margin: 0 auto;
img {
width: 100%;
}
}
.fz-player-controls-panel {
width: 100%;
height: 0.68rem;
background: rgba(23, 23, 26, 0.6);
position: absolute;
bottom: 0;
transition: opacity linear 0.2s;
opacity: 0;
.progress-wrapper {
width: 100%;
height: 100%;
display: flex;
align-items: center;
.mini-play-btn-wrapper {
width: 0.36rem;
height: 0.36rem;
margin: 0 0.24rem;
img {
width: 100%;
}
}
.full-btn-wrapper {
width: 0.36rem;
height: 0.36rem;
margin: 0 0.24rem;
img {
width: 100%;
}
}
.current-time {
width: 0.59rem;
height: 100%;
margin-right: 0.24rem;
font-size: 0.22rem;
color: #fff;
display: flex;
align-items: center;
flex-flow: row-reverse;
}
.duration-time {
width: 0.59rem;
height: 100%;
margin-left: 0.24rem;
font-size: 0.22rem;
color: #fff;
display: flex;
align-items: center;
}
.progress {
width: 4.39rem;
height: 100%;
position: relative;
.progress-line {
width: 100%;
height: 0.04rem;
background: #ffffff;
border-radius: 0.02rem;
opacity: 0.4;
top: 50%;
transform: translateY(-50%);
left: 0;
right: 0;
position: absolute;
margin: 0 auto;
z-index: 1;
}
.progress-current-line {
height: 0.04rem;
background: linear-gradient(90deg, #2cd3d7 0%, #2acf6f 100%);
border-radius: 0.02rem;
width: 0;
top: 50%;
transform: translateY(-50%);
left: 0;
right: 0;
position: absolute;
z-index: 2;
}
.current-indicator-wrapper {
top: 50%;
transform: translateY(-50%) translateX(-0.16rem);
position: absolute;
z-index: 3;
width: 0.32rem;
height: 0.32rem;
transition: left linear 0.03s;
.current-indicator {
width: 100%;
height: 100%;
background: linear-gradient(90deg, #2cd3d7 0%, #2acf6f 100%);
opacity: 0.4;
border-radius: 50%;
}
.current-indicator-inner {
width: 0.2rem;
height: 0.2rem;
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 0;
right: 0;
margin: 0 auto;
border-radius: 50%;
background: linear-gradient(90deg, #2cd3d7 0%, #2acf6f 100%);
}
}
}
}
}
}
}
说明 样式采用了less,单位使用rem,写样式用这些loader真的很方便。
js:
javascript
export default {
name: 'videoTest',
data() {
return {
// 是否展示control-pannel
showPannel: false,
// 是否展示start play btn
showStartPlay: true,
// 视频资源地址
videoSrc:
'https://cdn.cn/dubbing/2018-08-01/test.mp4',
// 视频封面地址
poster:
'https://cdn.cn/2024-03-19/test.jpg-thumb',
video: {
// 定义视频相关属性 自身
target: null,
// 当前时间
currentTime: 0,
// 当前时间展示字符串
currentTimeString: '00:00',
// 视频时长
duration: 0,
// 视频时长展示字符串
durationString: '00:00',
// 视频播放状态
status: 'pause',
// 进度条进度百分比
progress: 0,
// 视频是否开始播放
starting: false,
// control-pannel展示时间
showTime: 2000,
// control-pannel展示及时器
showTimer: null,
// 拖动的时间
offsetCurrentTime: 0
}
}
},
created() {},
mounted() {
// 加载视频信息
this.LoadVideo(this.$refs.video)
},
methods: {
LoadVideo(video) {
// 初始化视频属性及事件
},
GetTime(duration) {
// 时间展示处理
const minute = ~~(duration / 60)
const second = ~~(duration % 60)
const hour = ~~((duration - second) / 60 / 60)
const ms = minute > 9 ? minute : `0${minute}`
const ss = second > 9 ? second : `0${second}`
const hs = hour > 9 ? hour : `0${hour}`
return hour === 0 ? [ms, ss].join(':') : [hs, ms, ss].join(':')
},
Seek(e) {
// seek通用
},
TouchStart(e) {
// indicator拖动开始
},
TouchMove(e) {
// indicator拖动
},
TouchEnd(e) {
// indicator拖动结束
},
ShowControls(time = 2000) {
// indicator拖动开始
},
ShowControl() {
// control-pannel展示逻辑
},
PlayVideo() {
// 播放视频通用
},
SetFullScreen() {
// 全屏播放
}
}
}
说明 js这里的实现很偏面相对象了,实际上只是定义了整个播放器的属性和一些方法,这种编程思路比较符合我们正常人的思维模式,一般都是自上而下,从宏观到微观,当我们想好了我们的功能用例都需要哪些属性和方法之后,那么基本等于我们的工作完成了一大半,除非遇到个别技术细节,否则能把开发效率提高到一个很高的level。
总结
html+css基本是前端的身体,js才是灵魂,本篇我们已经把我们demo的身体和灵魂定义好了,下一篇即实现我们灵魂的升华~