微信小程序之自定义video组件
目录
微信小程序之自定义video组件
自定义video
小程序项目中使用到video组件,并且根据需求自定义播放、进度条和全屏按钮,本次需求比较简单,没有涉及试看,指定时长等功能,仅是简单的播放,记录一下,以防遗忘。贴下video组件的文档链接:
大概效果:
本文中用到的一些比较重要的属性和API
duration:指定视频时长(总时长)
controls:是否显示默认播放控件(播放/暂停按钮、播放进度、时间)
poster:视频封面
bindended:当视频播放到末尾触发
bindtimeupdate:视频进度发生变化时触发
videoContext:创建VideoContext实例,与video标签的id绑定,
进度条用到了 组件
话不多说,贴代码
wxml:(结构有些跟业务相关,实际按需求来实现)
<view class="video_area">
<view class="cover">
<video id="video"
class="video_wrapper"
src="{{videoSrc}}"
controls="{{false}}"
poster="{{poster}}"
show-center-play-btn="{{false}}"
custom-cache="{{false}}"
object-fit="contain"
enable-progress-gesture="{{false}}"
vslide-gesture-in-fullscreen="{{false}}"
bindtimeupdate="bindtimeupdateFun"
bindtap="playStart"
bindended="bindEndedFun">
<block>
<view wx:if="{{startPlay}}" class="cover_bg"></view>
<view wx:elif="{{play}}" class="cover_bg playState"></view>
<view wx:else class="cover_bg pauseState"></view>
</block>
<!-- 首次会有一个播放按钮 -->
<image wx:if="{{startPlay}}" src="./images/play_begin.png" mode="widthFix" class="play_begin" catchtap="playStart" data-type="init"/>
<!-- 视频中间的播放按钮 -->
<image wx:if="{{!play && !startPlay}}" src="./images/play.svg" class="play" mode="widthFix" catchtap="playStart"/>
<!-- 进度条相关 -->
<view class="process-area" wx:if="{{!startPlay}}">
<image src="{{!play ? './images/play_min.svg' : './images/pause.svg'}}" class="play_min" mode="widthFix" catchtap="playStart"/>
<text class="process_time time {{full && 'fullScreen'}}">{{process_duration}}</text>
<view class="slider_container">
<slider bindchange="sliderChange"
bindchanging="sliderChanging"
step="0.5"
backgroundColor="#D8D8D8" activeColor="#FF835B"
block-size="12"
value="{{sliderValue}}" />
</view>
<text class="total_process time {{full && 'fullScreen'}}">{{total_duration}}</text>
<image src="{{!full? './images/fullScreen.svg' : './images/exit.svg'}}" class="screen_btn" mode="widthFix" catchtap="fullScreen"/>
</view>
</video>
</view>
</view>
js
const { addZero } = require('../../utils/util'); // 格式化时间
let app = getApp();
Component({
options: {
styleIsolation: 'apply-shared'
},
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
poster: 'https://xxx/7831600741950_.pic.jpg', // 视频封面
videoSrc: 'https://xxx/1600742007827574.mp4', // 视频地址
startPlay: true, // 首次播放显示 播放视频按钮。
play: false, // 播放true 暂停 false
sliderValue: 0, // 进度条的value
process_duration: '00:00', // 进度
total_duration: '00:00', // 总时长
full: false, // 全屏
duration: '', // 视频长度 单位秒
},
ready() {
this.videoContext = wx.createVideoContext('video', this); // 创建videoContext实例
this.updateState = true; // 防止在播放过程中拖拽失败
},
/**
* 组件的方法列表
*/
methods: {
// 播放
playStart(e) {
const {
type = null
} = e.currentTarget.dataset;
if (type && type == 'init') {
// 开始播放
this.setData({
startPlay: false,
play: true
})
this.videoContext.play();
return false;
}
this.data.play ? this.videoContext.pause() : this.videoContext.play();
this.setData({
play: !this.data.play,
startPlay: false,
})
},
// 全屏/退出全屏
fullScreen() {
!this.data.full ? this.videoContext.requestFullScreen() : this.videoContext.exitFullScreen();
this.setData({
full: !this.data.full
})
},
// 完成一次拖动后触发的事件
sliderChange(e) {
if (this.data.duration) {
// 视频跳转到指定位置
this.videoContext.seek(e.detail.value / 100 * this.data.duration);
this.updateState = true;
this.setData({
sliderValue: e.detail.value
})
}
},
// 拖动过程中触发的事件
sliderChanging(e) {
this.updateState = false; // 拖动过程中不允许更新进度条
},
// 播放进度
bindtimeupdateFun(e) {
if (this.updateState) {
let sliderValue = e.detail.currentTime / e.detail.duration * 100;
this.setData({
sliderValue,
duration: e.detail.duration,
total_duration: this.formatSeconds(e.detail.duration), // 总时长 格式 00:00
process_duration: this.formatSeconds(e.detail.currentTime), // 进度 格式 00:00
})
}
},
// 结束
bindEndedFun(){
this.setData({
play: false
})
},
// 格式化视频时长 格式 00:00
formatSeconds(value) {
if (value == undefined) {
value = 0;
}
var second = parseInt(value); // 秒
var min = 0; // 分
var hour = 0; // 小时
if (second > 60) {
min = parseInt(second / 60);
second = parseInt(second % 60);
if (min > 60) {
hour = parseInt(min / 60);
min = parseInt(min % 60);
}
}
var result = "";
// if (hour > 0) {
// hour = addZero(parseInt(hour));
// result = hour + ":";
// }
if (min > 0) {
min = addZero(parseInt(min));
result = min + ":";
}else{
result = "00:";
}
if(second > 0){
second = addZero(parseInt(second));
result = result + second;
}else{
result = result + '00';
}
return result;
},
}
})
utils/util.js
addZero = (i) => {
i = typeof i === 'string' ? Number(i) : i;
return i < 10 ? "0" + i : "" + i;
}
wxss
.video_area{
width: 100%;
height: 350rpx;
position: relative;
}
.cover{
position: absolute;
top: 0;
left: -62rpx;
width: 620rpx;
height: 350rpx;
border-radius: 20rpx;
box-shadow: 0 7rpx 30rpx rgba(136,136,136,.6);
overflow: hidden;
}
.video_area .video_wrapper{
width:100%;
height: 100%;
position: relative;
border-radius: 20rpx;
}
.cover_bg{
width: 100%;
height: 100%;
opacity: 0.3;
background: #000;
position: absolute;
top: 0;
left: 0;
z-index: 10;
}
.cover_bg.playState{
opacity: 0.75;
background: linear-gradient(180deg, rgba(0,0,0,0.00) 79%, #040404 98%);
}
.cover_bg.pauseState{
opacity: 0.5;
background: #000;
}
.video_area .play_begin{
width: 234rpx;
height: auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 20;
}
.video_area .play{
width: 71rpx;
height: auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 20;
}
.process-area{
width: 100%;
height: 60rpx;
padding: 0 4% 0 4%;
position: absolute;
bottom: 13rpx;
left: 0;
display: flex;
align-items: center;
box-sizing: border-box;
z-index: 20;
}
.process-area .time{
display: inline-block;
flex:1;
color: #fff;
opacity: 0.95;
font-family: PingFangSC-Regular;
font-size: 14rpx;
color: #F4F3F0;
}
.process-area .time.fullScreen{
font-size: 18rpx
}
.process-area .time.process_time{
text-align: right;
margin-right: -10rpx;
}
.process-area .time.total_process{
text-align: left;
margin-left: -14rpx;
}
.play_min{
max-width: 24rpx;
max-height: 24rpx;
height: auto;
flex: 1;
display: inline-block;
padding: 20rpx;
margin:-20rpx;
}
.screen_btn{
max-width: 24rpx;
max-height: 24rpx;
height: auto;
flex: 1;
display: inline-block;
padding: 20rpx;
margin:-20rpx;
}
.slider_container{
flex:6;
}
—2020 0925
额,测试的时候发现个问题,安卓手机在暂停的播放时,拖动进度条,不会触发bindtimeupdate方法,如果直接拖到进度结束,也不会触发video的bindended。不知道有没有大佬们遇到过这个问题,坑啊。。。