1205 lines
29 KiB
Plaintext
1205 lines
29 KiB
Plaintext
<template>
|
||
<view class="skin-wrap">
|
||
<view class="plv-player-comp-box" :style="{
|
||
'width': (holeWidth) + 'px'
|
||
}">
|
||
<view class="plv-player-longpress-speed" v-if="isInLongPress && !isLock">
|
||
<image class="plv-player-longpress-speed__image" src="@/static/skin/forward.png"></image>
|
||
<text class="plv-player-longpress-speed__text">快进x2.0</text>
|
||
</view>
|
||
<drag-tips class="plv-player-skin-drag-tips" v-if="showGestureSeekTips" :duration="duration" :changeTime="gestureChangeSeekTime" :currentTime="currentTime"></drag-tips>
|
||
</view>
|
||
<gesture class="plv-player-mask"
|
||
:holeWidth="holeWidth"
|
||
:duration="duration"
|
||
:currentTime="currentTime"
|
||
@onLongPress="handleLongPress"
|
||
@onTouchCancel="handleTouchCancel"
|
||
@onGestureClick="handleMask"
|
||
@onTouchEnd="handleTouchEnd"
|
||
@onGestureSeekTo="handleSeekTo"
|
||
@onGestureEvent="handleGEvent">
|
||
</gesture>
|
||
<light class="plv-player-light" :imgUrl="lightImg" :precent="lightPrecent"></light>
|
||
<volume class="plv-player-volume" :imgUrl="volumeImg" :muteImgUrl="volumeMuteImg" :value="volumeValue"></volume>
|
||
<view class="plv-player-subtitle-view" v-if="isShowSubtitle">
|
||
<text v-if="isShowDoubleSubTitle" :style="{ fontWeight: topSubtitle.isBold? 'bold' : 'normal',
|
||
fontStyle: topSubtitle.isItalic? 'italic' : 'normal', color: topSubtitle.fontColor,
|
||
backgroundColor: topSubtitle.bgColor, fontSize: topSubtitle.fontSize }">{{topSubtitle.text}}</text>
|
||
<text v-if="isShowDoubleSubTitle" :style="{ fontWeight: bottomSubtitle.isBold? 'bold' : 'normal',
|
||
fontStyle: bottomSubtitle.isItalic? 'italic' : 'normal', color: bottomSubtitle.fontColor,
|
||
backgroundColor: bottomSubtitle.bgColor, fontSize: bottomSubtitle.fontSize }">{{bottomSubtitle.text}}</text>
|
||
<text v-else="isShowSingleSubTitle" :style="{ fontWeight: singleSubtitle.isBold? 'bold' : 'normal',
|
||
fontStyle: singleSubtitle.isItalic? 'italic' : 'normal', color: singleSubtitle.fontColor,
|
||
backgroundColor: singleSubtitle.bgColor, fontSize: singleSubtitle.fontSize }">{{singleSubtitle.text}}</text>
|
||
</view>
|
||
<view class="plv-player-bar" v-if="!selectStatus && showBar && !isLock">
|
||
<image class="plv-player-bar__bg" :src="bottomBar"></image>
|
||
<view class="plv-player-bar__box">
|
||
<view class="plv-player-mr-left"></view>
|
||
<view class="plv-player-middle">
|
||
<view class="plv-player-progress">
|
||
<view class="plv-player-progress__play__wrap">
|
||
<view class="plv-player-progress__wrap__played" :style="'flex:' + progressPrecent"></view>
|
||
<view class="plv-player-progress__wrap__surplus" :style="'flex:' + progressLeftOverPrecent"></view>
|
||
</view>
|
||
<view class="plv-player-progress__dot__wrap">
|
||
<view class="plv-player-progress__dot__box">
|
||
<view class="plv-player-progress__dot__played" :style="'flex:' + (progressPrecent || 1)">
|
||
<view class="plv-player-progress__dot"></view>
|
||
</view>
|
||
<view class="plv-player-progress__dot__surplus" :style="'flex:' + progressLeftOverPrecent"></view>
|
||
</view>
|
||
</view>
|
||
<view class="plv-player-progress__mask"
|
||
@touchstart="touchstart"
|
||
@touchmove="touchmove"
|
||
@touchend="touchend"></view>
|
||
</view>
|
||
<view class="plv-player-bar-bottom">
|
||
<image class="plv-btn plv-player-play-btn" :src=playBtnLink @click="handlePlay"></image>
|
||
<view class="plv-player-time">
|
||
<text class="plv-player-btn__text">{{ currentTimeText }}</text>
|
||
<text class="plv-player-btn__text"> / </text>
|
||
<text class="plv-player-btn__text">{{ durationText }}</text>
|
||
</view>
|
||
<image class="plv-btn plv-player-full-btn" :src="fullBtnLink" @click="handleFull"></image>
|
||
<text class="plv-btn plv-player-hd-btn plv-player-btn__text" @click="handleBtn(types.LEVEL)">{{ currentLevelText }}</text>
|
||
<text class="plv-btn plv-player-rate-btn plv-player-btn__text" @click="handleBtn(types.SPEED)">{{ currentSpeedText }}</text>
|
||
<text class="plv-btn plv-player-scaling-btn plv-player-btn__text" @click="handleBtn(types.SCALING)">画面</text>
|
||
<text class="plv-btn plv-player-subtitle-btn plv-player-btn__text"
|
||
v-if="isEnableSubTitle" @click="handleBtn(types.SUBTITLE)">字幕</text>
|
||
</view>
|
||
</view>
|
||
<view class="plv-player-mr-right"></view>
|
||
</view>
|
||
</view>
|
||
<view class="plv-player-select" v-if="selectStatus">
|
||
<view class="plv-player-select__bg"></view>
|
||
<view class="plv-player-select__option">
|
||
<text v-for=" (item, index) in selects"
|
||
:key="index"
|
||
class="plv-player-select__option__item"
|
||
:class="{ 'select-option__text': true, active: index === currentOptionIndex}"
|
||
@click="selectOption(index)">
|
||
{{ item[0] }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
<view class="plv-player-screenshot" v-if="!selectStatus && showBar && !isLock" :style="'bottom:' + screenShotY">
|
||
<image class="plv-player-screenshot__btn" :src="screenShotImg" @click="$emit('onScreenShot')"></image>
|
||
</view>
|
||
<view class="plv-player-lock" v-if="!selectStatus && showBar" :style="'bottom:' + screenShotY">
|
||
<image class="plv-player-lock__btn" :src="lockImgSrc" @click="isLock = !isLock"></image>
|
||
</view>
|
||
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import Gesture from './gesture.nvue';
|
||
import Light from './light.nvue';
|
||
import Volume from './volume.nvue';
|
||
import DragTips from '@/components/plv-player-skin/drag-tips.nvue';
|
||
import LockImg from '@/static/skin/lock.png';
|
||
import UnLockImg from '@/static/skin/unlock.png';
|
||
// 部分img 放到了static/skin目录下
|
||
const imgLink = {
|
||
play: 'https://play1.polyv.net/player2/demo/uniapp/pause-left-btn.png',
|
||
pause: 'https://play1.polyv.net/player2/demo/uniapp/play-left-btn.png',
|
||
full: 'https://play1.polyv.net/player2/demo/uniapp/fullscreen-btn.png',
|
||
normal: 'https://play1.polyv.net/player2/demo/uniapp/normalscreen-btn.png',
|
||
bottomBar: 'https://play1.polyv.net/player2/demo/uniapp/bottombar.png',
|
||
light: 'https://play1.polyv.net/player2/demo/uniapp/light.png',
|
||
volume: 'https://play1.polyv.net/player2/demo/uniapp/volume.png',
|
||
mute: 'https://play1.polyv.net/player2/demo/uniapp/mute.png',
|
||
screenShot: 'https://play1.polyv.net/player2/demo/uniapp/screenShot.png'
|
||
};
|
||
|
||
export default {
|
||
components: {
|
||
Gesture,
|
||
Light,
|
||
Volume,
|
||
DragTips
|
||
},
|
||
|
||
data() {
|
||
return {
|
||
// 是否显示控制栏
|
||
showBar: true,
|
||
// 控制栏自动渐隐时间(单位:秒)
|
||
showBarTime: 10,
|
||
// 播放状态
|
||
playStatus: true,
|
||
// 播放按钮图片
|
||
playBtnLink: imgLink.play,
|
||
// 全屏状态
|
||
fullStatus: false,
|
||
// 全屏按钮图片
|
||
fullBtnLink: imgLink.full,
|
||
// 控制栏渐变背景图
|
||
bottomBar: imgLink.bottomBar,
|
||
// 时间显示块
|
||
timeShowBlock: null,
|
||
// 当前播放时间
|
||
currentTime: null,
|
||
currentTimeText: '00:00',
|
||
// 总时长
|
||
duration: null,
|
||
durationText: null,
|
||
// 播放进度占总时长百分比
|
||
precent: 0,
|
||
// 进度条播放进度计算的百分比
|
||
progressPrecent: 0,
|
||
// 进度条播放进度计算的剩余百分比
|
||
progressLeftOverPrecent: 100,
|
||
// 是否正在拖拽中
|
||
isDraging: false,
|
||
// 选择面板中所有选项
|
||
selects: [],
|
||
// 选择面板是否显示中
|
||
selectStatus: false,
|
||
// 选择面板选中类型 levels:清晰度 speeds:倍速
|
||
types: {
|
||
LEVEL: 'level',
|
||
SPEED: 'speed',
|
||
SCALING: 'scaling',
|
||
SUBTITLE: 'subtitle'
|
||
},
|
||
// 当前选中类型
|
||
type: null,
|
||
// 可选码率
|
||
levels: [
|
||
['自动', 0],
|
||
['流畅', 1],
|
||
['高清', 2],
|
||
['超清', 3]
|
||
],
|
||
// 当前码率
|
||
level: 0,
|
||
// 点击了自动码率选项
|
||
selectedHdAuto: false,
|
||
// 可选倍速
|
||
speeds: [
|
||
['0.5x', 0.5],
|
||
['1x', 1],
|
||
['1.5x', 1.5],
|
||
['2x', 2]
|
||
],
|
||
// 当前倍速
|
||
speed: 1,
|
||
// 亮度图片
|
||
lightImg: imgLink.light,
|
||
// 亮度百分比
|
||
lightPrecent: 0,
|
||
// 音量图片
|
||
volumeImg: imgLink.volume,
|
||
volumeMuteImg: imgLink.mute,
|
||
// 音量值
|
||
volumeValue: 0.5,
|
||
//截图按钮
|
||
screenShotImg: imgLink.screenShot,
|
||
// 截图按钮距离底部高度
|
||
screenShotY: '200rpx',
|
||
|
||
showGestureSeekTips: false,
|
||
// 手势拖动时
|
||
gestureChangeSeekTime: 0,
|
||
|
||
seekedNeedPlay: false,
|
||
// 是否锁屏
|
||
isLock: false,
|
||
// 视频拉伸模式文案
|
||
scalings: [['居中', 0], ['适应', 1], ['填充',2], ['拉伸', 3]],
|
||
// 拉伸模式
|
||
scaling: undefined,
|
||
// 是否处于长按的状态
|
||
isInLongPress: false,
|
||
// 是否开启字幕功能
|
||
isEnableSubTitle: true,
|
||
isEnableDoubleSubTitle: false,
|
||
subtitles: [['不显示',0]],
|
||
isShowSubtitle: false,
|
||
isShowSingleSubTitle: false,
|
||
isShowDoubleSubTitle:false,
|
||
subtitle: 0,
|
||
topSubtitle: {
|
||
text:"",
|
||
isBold: false,
|
||
isItalic: false,
|
||
fontColor:"#fb12d4",
|
||
bgColor:"#67f933",
|
||
fontSize: 14
|
||
},
|
||
bottomSubtitle: {
|
||
text:"",
|
||
isBold: false,
|
||
isItalic: false,
|
||
fontColor:"#ffff00",
|
||
bgColor:"#00f3ff",
|
||
fontSize: 14
|
||
},
|
||
singleSubtitle: {
|
||
text:"",
|
||
isBold: false,
|
||
isItalic: false,
|
||
fontColr:"#ffffff",
|
||
bgColor: "#000000",
|
||
fontSize: 14
|
||
}
|
||
}
|
||
},
|
||
|
||
watch: {
|
||
type() {
|
||
this.selects = this[`${this.type}s`];
|
||
}
|
||
},
|
||
|
||
computed: {
|
||
currentLevelText() {
|
||
const { levels, level } = this;
|
||
return levels[level][0];
|
||
},
|
||
|
||
currentSpeedText() {
|
||
const { speeds, speed } = this;
|
||
return speeds[speed][0];
|
||
},
|
||
|
||
currentOptionIndex() {
|
||
const { type, types} = this;
|
||
|
||
switch(type) {
|
||
case types.LEVEL: {
|
||
return this.level;
|
||
}
|
||
case types.SPEED: {
|
||
return this.speed;
|
||
}
|
||
case types.SCALING: {
|
||
return this.scaling;
|
||
}
|
||
case types.SUBTITLE: {
|
||
return this.subtitle;
|
||
}
|
||
}
|
||
|
||
// if (type === types.LEVEL)
|
||
// return this.level;
|
||
// else
|
||
// return this.speed;
|
||
},
|
||
|
||
lockImgSrc() {
|
||
return this.isLock ? LockImg: UnLockImg;
|
||
}
|
||
},
|
||
|
||
mounted() {
|
||
this.countHoleWidth();
|
||
this.barHide();
|
||
},
|
||
|
||
methods: {
|
||
/**
|
||
* 计算进度条总长度
|
||
*/
|
||
countHoleWidth() {
|
||
const { fullStatus } = this;
|
||
|
||
const height = plus.display.resolutionHeight;
|
||
const width = plus.display.resolutionWidth;
|
||
|
||
|
||
if (fullStatus)
|
||
this.holeWidth = height > width ? height : width;
|
||
|
||
else
|
||
this.holeWidth = width < height ? width : height;
|
||
},
|
||
|
||
/**
|
||
* Toogle修改播放按钮状态,传入forceStatus以forceStatus为主
|
||
* @param {Object} forceStatus
|
||
*/
|
||
changePlayStatus(forceStatus) {
|
||
const { playStatus } = this;
|
||
let newStatus = !playStatus;
|
||
if (typeof forceStatus !== 'undefined')
|
||
newStatus = forceStatus;
|
||
|
||
this.playStatus = newStatus;
|
||
this.playBtnLink = newStatus ? imgLink.play : imgLink.pause;
|
||
},
|
||
|
||
pause() {
|
||
this.playStatus = true;
|
||
this.handlePlay();
|
||
},
|
||
|
||
play() {
|
||
this.playStatus = false;
|
||
this.handlePlay();
|
||
},
|
||
|
||
/**
|
||
* 播放按钮点击
|
||
*/
|
||
handlePlay() {
|
||
const { playStatus } = this;
|
||
this.changePlayStatus();
|
||
this.$emit('onPlayBtnClick', this.playStatus);
|
||
},
|
||
|
||
/**
|
||
* Toogle修改全屏按钮状态
|
||
*/
|
||
changeFullStatus() {
|
||
const { fullStatus } = this;
|
||
let newStatus = !fullStatus;
|
||
|
||
this.fullStatus = newStatus;
|
||
this.fullBtnLink = newStatus ? imgLink.normal : imgLink.full;
|
||
},
|
||
|
||
/**
|
||
* 全屏按钮点击
|
||
*/
|
||
handleFull() {
|
||
const { fullStatus } = this;
|
||
this.changeFullStatus();
|
||
this.$emit('onFullBtnClick', this.fullStatus);
|
||
this.countHoleWidth();
|
||
|
||
this.screenShotY = fullStatus ? '200rpx' : '300rpx';
|
||
|
||
},
|
||
|
||
/**
|
||
* 点击清晰度/倍速按钮
|
||
*/
|
||
handleBtn(type) {
|
||
const { selectStatus } = this;
|
||
this.type = type;
|
||
this.selectStatus = !this.selectStatus;
|
||
},
|
||
|
||
/**
|
||
* 选择切换清晰度/倍速
|
||
* @param {Object} value
|
||
*/
|
||
selectOption(value) {
|
||
const { types, type, speeds, subtitles } = this;
|
||
|
||
this[type] = value;
|
||
this.selectStatus = false;
|
||
|
||
switch(type) {
|
||
case types.LEVEL:
|
||
this.selectedHdAuto = value === 0;
|
||
this.$emit('onHdBtnClick', value);
|
||
break;
|
||
case types.SPEED:
|
||
this.$emit('onRateBtnClick', speeds[value][1]);
|
||
break;
|
||
case types.SCALING:
|
||
this.$emit('onScalingBtnClick', value);
|
||
break;
|
||
case types.SUBTITLE:
|
||
if(value === 0) {
|
||
this.isShowSubtitle = false;
|
||
this.$emit('onOpenSRT', false);
|
||
this.isShowDoubleSubTitle = false;
|
||
this.isShowSingleSubTitle = false;
|
||
break;
|
||
}
|
||
if (subtitles[value][0] === "双语") {
|
||
console.log("===1 切换双语")
|
||
// 发送开启双字幕
|
||
this.isShowSubtitle = true;
|
||
this.isShowDoubleSubTitle = true;
|
||
this.isShowSingleSubTitle = false;
|
||
this.$emit('onChangeSRTSingleMode', false);
|
||
this.$emit('onOpenSRT', true);
|
||
break;
|
||
}
|
||
this.isShowSubtitle = true;
|
||
this.isShowDoubleSubTitle = false;
|
||
this.isShowSingleSubTitle = true;
|
||
this.$emit('onChangeSRTSingleMode', true);
|
||
this.$emit('onOpenSRT', true);
|
||
this.$emit('onChangeSRTBtnClick', subtitles[value][0]);
|
||
break;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 点击空白区域
|
||
*/
|
||
handleMask() {
|
||
const { selectStatus, showBar } = this;
|
||
/**
|
||
* 若清晰度显示则隐藏
|
||
*/
|
||
if (selectStatus) {
|
||
this.selectStatus = false;
|
||
return;
|
||
}
|
||
|
||
if (showBar) {
|
||
this.showBar = false;
|
||
} else {
|
||
this.showBar = true;
|
||
this.barHide();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 当前播放时间更新
|
||
* @param {Object} time
|
||
*/
|
||
timeUpdate(time) {
|
||
this.currentTime = time;
|
||
this.currentTimeText = this.timeFormat(time);
|
||
this.updateProgress();
|
||
},
|
||
|
||
/**
|
||
* 是否有字幕功能
|
||
* @param {boolean} enableSubTitle
|
||
* @param {boolean} enableSubTitleDouble
|
||
*/
|
||
setEnableSubtitle(enableSubTitle, enableSubTitleDouble) {
|
||
this.isEnableSubTitle = enableSubTitle
|
||
this.enableSubTitleDouble = enableSubTitleDouble
|
||
const { subtitles } = this;
|
||
if (enableSubTitleDouble) {
|
||
this.subtitles.push(["双语", subtitles.length]);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 设置字幕的样式
|
||
* @param {Object} config
|
||
*/
|
||
setSRTTextConfig(config) {
|
||
const position = config.position;
|
||
console.log("===1 setSRTTextConfig position: " + config.position);
|
||
console.log("===1 setSRTTextConfig color: " + config.backgroundColor);
|
||
const color = this.convertARGBtoRGBA(config.backgroundColor);
|
||
console.log("===1 change color: " + color);
|
||
if (position === "bottom") {
|
||
this.bottomSubtitle.fontColor = this.convertARGBtoRGBA(config.fontColor);
|
||
this.bottomSubtitle.fontSize = config.fontSize;
|
||
this.bottomSubtitle.bgColor = this.convertARGBtoRGBA(config.backgroundColor);
|
||
this.bottomSubtitle.isBold = config.fontBold;
|
||
this.bottomSubtitle.isItalic = config.fontItalics;
|
||
}
|
||
|
||
if (position === "top") {
|
||
this.topSubtitle.fontColor = this.convertARGBtoRGBA(config.fontColor);
|
||
this.topSubtitle.fontSize = config.fontSize;
|
||
this.topSubtitle.bgColor = this.convertARGBtoRGBA(config.backgroundColor);
|
||
this.topSubtitle.isBold = config.fontBold;
|
||
this.topSubtitle.isItalic = config.fontItalics;
|
||
}
|
||
|
||
if (position === null || position === "") {
|
||
this.singleSubtitle.fontColor = this.convertARGBtoRGBA(config.fontColor);
|
||
this.singleSubtitle.fontSize = config.fontSize;
|
||
this.singleSubtitle.bgColor = this.convertARGBtoRGBA(config.backgroundColor);
|
||
this.singleSubtitle.isBold = config.fontBold;
|
||
this.singleSubtitle.isItalic = config.fontItalics;
|
||
}
|
||
},
|
||
|
||
convertARGBtoRGBA(argbValue) {
|
||
if(!argbValue.startsWith('#') || argbValue.length !== 9) {
|
||
return argbValue;
|
||
}
|
||
const hexValue = argbValue.replace('#', '');
|
||
const alpha = parseInt(hexValue.slice(0, 2), 16) / 255;
|
||
const red = parseInt(hexValue.slice(2, 4), 16);
|
||
const green = parseInt(hexValue.slice(4, 6), 16);
|
||
const blue = parseInt(hexValue.slice(6, 8), 16);
|
||
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
||
},
|
||
|
||
/**
|
||
* 插入当前有多少可选的字幕
|
||
* @param {Object} titles
|
||
*/
|
||
setSRTTitle(titles) {
|
||
console.log("===1 title is " + titles);
|
||
const newItems = titles.map((lang)=>[lang, titles.indexOf(lang) + this.subtitles.length + 1]);
|
||
this.subtitles.push(...newItems);
|
||
},
|
||
|
||
/**
|
||
* 字幕
|
||
* @param {Object} srt
|
||
*/
|
||
setSRTText(srt) {
|
||
this.singleSubtitle.text = "";
|
||
this.topSubtitle.text = "";
|
||
this.bottomSubtitle.text = "";
|
||
|
||
console.info("====3 srtList: " + srt);
|
||
|
||
const data = JSON.parse(srt);
|
||
if (!data) {
|
||
return;
|
||
}
|
||
data.strList.forEach(item => {
|
||
const position = item.position;
|
||
if (position === "singleSubtitles") {
|
||
this.singleSubtitle.text = item.srtText;
|
||
}
|
||
if (position === "bottomSubtitles") {
|
||
this.bottomSubtitle.text = item.srtText;
|
||
}
|
||
if (position === "topSubtitles") {
|
||
this.topSubtitle.text = item.srtText;
|
||
}
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 视频总时长更新
|
||
* @param {Object} duration
|
||
*/
|
||
updateDuration(duration) {
|
||
this.duration = duration;
|
||
this.timeShowBlock = duration >= 3600 ? 2 : 1;
|
||
this.durationText = this.timeFormat(duration);
|
||
},
|
||
|
||
/**
|
||
* 更新进度条显示
|
||
*/
|
||
updateProgress(precent) {
|
||
const { currentTime, duration, finalPrecent, isDraging } = this;
|
||
let _precent;
|
||
|
||
const changeProgressWidth = (_precent) => {
|
||
this.progressPrecent = parseInt( _precent * 100 );
|
||
this.progressLeftOverPrecent = 100 - this.progressPrecent;
|
||
};
|
||
|
||
if (precent) {
|
||
// 强制使用外部传入进度百分比
|
||
_precent = this.limit(precent, 0, 1);
|
||
changeProgressWidth(_precent);
|
||
} else {
|
||
// 计算timeupdate当前进度条百分比
|
||
if (!duration || isDraging) return;
|
||
_precent = currentTime / duration;
|
||
this.precent = _precent;
|
||
changeProgressWidth(_precent);
|
||
}
|
||
|
||
},
|
||
/**
|
||
* 更新视频画面缩放模式
|
||
*/
|
||
updateScaling(mode) {
|
||
this.scaling = mode;
|
||
},
|
||
|
||
/**
|
||
* 监听进度条拖拽seek
|
||
*/
|
||
handleTouch(type, e) {
|
||
switch(type) {
|
||
case 'start':
|
||
this.initTouchPageX = e.touches[0].pageX * 2;
|
||
this.barShow();
|
||
break;
|
||
case 'move':
|
||
this.isDraging = true;
|
||
const touchPageX = e.touches[0].pageX * 2;
|
||
const changedX = this.getChangedX(touchPageX, this.initTouchPageX);
|
||
this.calcPrecentChange(changedX);
|
||
break;
|
||
case 'end':
|
||
if (this.isDraging) {
|
||
this.toSeek(this.finalPrecent);
|
||
this.isDraging = false;
|
||
}
|
||
break;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 计算变化百分比
|
||
* @param {Object} changedX
|
||
*/
|
||
calcPrecentChange(changedX) {
|
||
const { precent, holeWidth } = this;
|
||
let finalPrecent;
|
||
|
||
const changedPrecent = changedX / holeWidth;
|
||
finalPrecent = precent + changedPrecent;
|
||
finalPrecent = this.limit(finalPrecent, 0, 1);
|
||
this.updateProgress(finalPrecent);
|
||
this.finalPrecent = finalPrecent;
|
||
},
|
||
|
||
/**
|
||
* 获取变化的X坐标
|
||
* @param {Object} touchPageX touchend触发后的X坐标
|
||
* @param {Object} initTouchPageX touchstart触发后的X坐标
|
||
*/
|
||
getChangedX(touchPageX, initTouchPageX) {
|
||
return touchPageX - initTouchPageX;
|
||
},
|
||
|
||
touchstart(e) {
|
||
this.handleTouch('start', e);
|
||
},
|
||
|
||
touchmove(e) {
|
||
this.handleTouch('move', e);
|
||
},
|
||
|
||
touchend(e) {
|
||
this.handleTouch('end', e);
|
||
},
|
||
|
||
toSeek(precent) {
|
||
const seekTime = parseInt(precent * this.duration);
|
||
if (!isNaN(seekTime)) this.$emit('onToSeek', seekTime);
|
||
},
|
||
|
||
handleSeekTo(time) {
|
||
if (!isNaN(time)) {
|
||
this.$emit('onToSeek', time);
|
||
setTimeout(() => {
|
||
if (this.seekedNeedPlay) {
|
||
this.play();
|
||
this.seekedNeedPlay = false;
|
||
}
|
||
}, 100)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 更新可选码率
|
||
* @param {Object} num
|
||
*/
|
||
updateLevels(num) {
|
||
if (!num) return;
|
||
const realNum = num + 1;
|
||
var allLevels = [
|
||
['自动', 0],
|
||
['流畅', 1],
|
||
['高清', 2],
|
||
['超清', 3]
|
||
];
|
||
this.levels = allLevels.slice(0, realNum);
|
||
if (this.type === this.types.LEVEL) {
|
||
this.selects = this.levels;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 更新当前码率
|
||
* @param {Object} level
|
||
*/
|
||
updateCurrentLevel(level) {
|
||
const { selectedHdAuto } = this;
|
||
if (selectedHdAuto) return;
|
||
// 若点击自动清晰度按钮后,不更新码率显示
|
||
this.level = level;
|
||
},
|
||
|
||
/**
|
||
* 显示控制栏
|
||
*/
|
||
barShow() {
|
||
if (this.clock) {
|
||
clearTimeout(this.clock);
|
||
this.clock = null;
|
||
}
|
||
this.showBar = true;
|
||
},
|
||
|
||
/**
|
||
* 隐藏控制栏
|
||
* @param {Boolean} immediately 是否立刻隐藏
|
||
*/
|
||
barHide(immediately) {
|
||
return;
|
||
const { showBarTime } = this;
|
||
if (this.clock) {
|
||
clearTimeout(this.clock);
|
||
this.clock = null;
|
||
}
|
||
|
||
this.clock = setTimeout(() => {
|
||
this.showBar = false;
|
||
}, showBarTime * 1000);
|
||
},
|
||
|
||
/**
|
||
* 处理手势控制
|
||
* @param {Object} type 手势类型
|
||
* @param {Object} value 手势变化值
|
||
*/
|
||
handleGEvent(data) {
|
||
switch(data.type) {
|
||
case 'SEEK_TIME_UPDATE': {
|
||
|
||
if (this.playStatus) {
|
||
this.seekedNeedPlay = true;
|
||
this.pause();
|
||
}
|
||
const { finalTime } = data;
|
||
if (isNaN(finalTime)) {
|
||
return;
|
||
}
|
||
this.gestureChangeSeekTime = finalTime;
|
||
this.showGestureSeekTips = true;
|
||
clearTimeout(this.$options.gestureTimeout);
|
||
|
||
// 600ms后隐藏tips
|
||
this.$options.gestureTimeout = setTimeout(() => {
|
||
this.showGestureSeekTips = false;
|
||
this.gestureChangeSeekTime = 0;
|
||
}, 600)
|
||
|
||
}
|
||
break;
|
||
case 'LEFT_UP':
|
||
// console.log('LEFT_UP');
|
||
uni.getScreenBrightness({
|
||
success: (res) => {
|
||
this.setBrightness(res.value, data.position, 1);
|
||
}
|
||
});
|
||
break;
|
||
case 'LEFT_DOWN':
|
||
// console.log('LEFT_DOWN');
|
||
uni.getScreenBrightness({
|
||
success: (res) => {
|
||
this.setBrightness(res.value, data.position, -1);
|
||
}
|
||
});
|
||
break;
|
||
case 'RIGHT_UP':
|
||
// console.log('RIGHT_UP');
|
||
this.volumeChanged(data.position, 1);
|
||
break;
|
||
case 'RIGHT_DOWN':
|
||
// console.log('RIGHT_DOWN');
|
||
this.volumeChanged(data.position, -1);
|
||
break;
|
||
case 'DOUBLE_CLICK':
|
||
console.log('DOUBLE_CLICK');
|
||
if (this.playStatus) {
|
||
this.pause();
|
||
} else {
|
||
this.play();
|
||
}
|
||
break;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 设置亮度
|
||
* @param {Object} currentValue 当前亮度
|
||
* @param {Object} changedValue 改变值
|
||
* @param {Object} isDown 若为减弱,则设置为-1,否则为1
|
||
*/
|
||
setBrightness(currentValue, changedValue, isDown) {
|
||
// 除6降低灵敏度
|
||
changedValue = Math.abs(changedValue / 6) / 100 * isDown;
|
||
let realValue = currentValue + changedValue;
|
||
realValue = this.limit(realValue, 0, 1);
|
||
this.lightPrecent = parseInt(realValue * 100);
|
||
|
||
uni.setScreenBrightness({
|
||
value: realValue
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 音量变化
|
||
* @param {Object} changedValue
|
||
* @param {Object} isDown
|
||
*/
|
||
volumeChanged(changedValue, isDown) {
|
||
changedValue = Math.abs(changedValue / 10) / 100 * isDown;
|
||
this.$emit('onVolumeChanged', changedValue);
|
||
},
|
||
|
||
/**
|
||
* 更新音量变化显示
|
||
* @param {Object} value
|
||
*/
|
||
updateVolumeValue(value) {
|
||
this.volumeValue = value;
|
||
},
|
||
|
||
/**
|
||
* 时间(秒)格式化
|
||
* @param {Object} result
|
||
*/
|
||
timeFormat(result) {
|
||
const { timeShowBlock } = this;
|
||
|
||
let h = Math.floor(result / 3600) < 10 ? '0' + Math.floor(result / 3600) : Math.floor(result / 3600);
|
||
let m = Math.floor((result / 60 % 60)) < 10 ? '0' + Math.floor((result / 60 % 60)) : Math.floor((result / 60 % 60));
|
||
let s = Math.floor((result % 60)) < 10 ? '0' + Math.floor((result % 60)) : Math.floor((result % 60));
|
||
|
||
if (timeShowBlock === 2)
|
||
return result = h + ':' + m + ':' + s;
|
||
else
|
||
return result = m + ':' + s;
|
||
},
|
||
|
||
/**
|
||
* 控制最小值,最大值
|
||
* @param num
|
||
* @param min
|
||
* @param max
|
||
* @returns {*}
|
||
*/
|
||
limit(num, min, max) {
|
||
if (num < min) return min;
|
||
if (num > max) return max;
|
||
return num;
|
||
},
|
||
|
||
handleTouchCancel() {
|
||
if (this.isLock) return;
|
||
if (this.isInLongPress) {
|
||
this.isInLongPress = false;
|
||
this.$emit('onRateBtnClick', 1);
|
||
}
|
||
|
||
},
|
||
// 特殊情况如果不触发touchcancel, 触发touchend时取消长按状态
|
||
handleTouchEnd() {
|
||
this.handleTouchCancel()
|
||
},
|
||
|
||
|
||
handleLongPress() {
|
||
if (this.isLock) return;
|
||
this.isInLongPress = true;
|
||
|
||
this.play();
|
||
this.$emit('onRateBtnClick', 2);
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.plv-player-mask {
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 120rpx;
|
||
}
|
||
|
||
.plv-player-bar__bg {
|
||
width: 1624rpx;
|
||
height: 120rpx;
|
||
}
|
||
|
||
.plv-player-bar {
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
height: 120rpx;
|
||
bottom: 0;
|
||
}
|
||
|
||
.plv-player-bar__box {
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
height: 120rpx;
|
||
bottom: 0;
|
||
flex-direction: row;
|
||
}
|
||
|
||
.plv-player-mr-left {
|
||
flex: 10;
|
||
}
|
||
|
||
.plv-player-mr-right {
|
||
flex: 10;
|
||
}
|
||
|
||
.plv-player-middle {
|
||
flex: 80;
|
||
}
|
||
|
||
.plv-player-progress {
|
||
flex: 40;
|
||
}
|
||
|
||
.plv-player-subtitle-view {
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
height: 150rpx;
|
||
bottom: 50rpx;
|
||
justify-content: center;
|
||
align-items: center;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.plv-subtitle-top-text {
|
||
background-color: greenyellow;
|
||
}
|
||
|
||
.plv-subtitle-bottom-text {
|
||
background-color: darkred;
|
||
}
|
||
|
||
.plv-player-progress__play__wrap {
|
||
flex-direction: row;
|
||
flex: 1;
|
||
}
|
||
|
||
.plv-player-progress__wrap__played {
|
||
top: 20rpx;
|
||
height: 8rpx;
|
||
background-color: #0A98D5;
|
||
}
|
||
|
||
.plv-player-progress__wrap__surplus {
|
||
top: 20rpx;
|
||
height: 8rpx;
|
||
background-color: #FFFFFF;
|
||
}
|
||
|
||
.plv-player-progress__dot__wrap {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
z-index: 1;
|
||
}
|
||
|
||
.plv-player-progress__dot__box {
|
||
flex: 1;
|
||
flex-direction: row;
|
||
}
|
||
|
||
.plv-player-progress__dot__played {
|
||
flex: 20;
|
||
}
|
||
|
||
.plv-player-progress__dot__surplus {
|
||
flex: 80;
|
||
}
|
||
|
||
.plv-player-progress__mask {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
left: 0;
|
||
bottom: 0;
|
||
border-radius: 4rpx;
|
||
z-index: 2;
|
||
}
|
||
|
||
.plv-player-progress__dot {
|
||
position: absolute;
|
||
width: 10rpx;
|
||
height: 30rpx;
|
||
top: 8rpx;
|
||
right: 0;
|
||
border-radius: 4rpx;
|
||
background-color: #FFFFFF;
|
||
/* transform: translateX(15rpx); */
|
||
}
|
||
|
||
.plv-player-bar-bottom {
|
||
flex-direction: row;
|
||
flex: 60;
|
||
}
|
||
|
||
.plv-btn {
|
||
position: relative;
|
||
width: 64rpx;
|
||
height: 64rpx;
|
||
top: 8rpx;
|
||
bottom: 0;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
.plv-player-full-btn {
|
||
position: absolute;
|
||
right: 0;
|
||
}
|
||
|
||
.plv-player-hd-btn {
|
||
position: absolute;
|
||
right: 64rpx;
|
||
}
|
||
|
||
.plv-player-rate-btn {
|
||
position: absolute;
|
||
right: 128rpx;
|
||
}
|
||
|
||
.plv-player-scaling-btn {
|
||
position: absolute;
|
||
right: 210rpx;
|
||
}
|
||
|
||
.plv-player-subtitle-btn {
|
||
position: absolute;
|
||
right: 278rpx;
|
||
}
|
||
|
||
.plv-player-time {
|
||
position: relative;
|
||
top: 8rpx;
|
||
flex-direction: row
|
||
}
|
||
|
||
.plv-player-select {
|
||
position: absolute;
|
||
width: 200rpx;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
z-index: 1;
|
||
}
|
||
|
||
.plv-player-select__bg {
|
||
position: absolute;
|
||
top: 0;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background-color: #0A98D5;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.plv-player-select__option {
|
||
position: absolute;
|
||
top: 0;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 20rpx;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.plv-player-select__option__item {
|
||
padding: 18rpx;
|
||
}
|
||
|
||
|
||
.plv-player-btn__text {
|
||
color: #FFFFFF;
|
||
font-size: 28rpx;
|
||
line-height: 64rpx;
|
||
}
|
||
|
||
.select-option__text {
|
||
color: #FFFFFF;
|
||
font-size: 28rpx;
|
||
line-height: 20rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.active {
|
||
color: #0A98D5;
|
||
}
|
||
|
||
.plv-player-light {
|
||
flex: 1;
|
||
align-items: center;
|
||
}
|
||
|
||
.plv-player-volume {
|
||
position: absolute;
|
||
top: 50rpx;
|
||
left: 0;
|
||
right: 0;
|
||
height: 80rpx;
|
||
}
|
||
|
||
.plv-player-screenshot {
|
||
position: absolute;
|
||
margin-left: 80rpx;
|
||
}
|
||
|
||
.plv-player-screenshot__btn {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
}
|
||
|
||
.plv-player-lock {
|
||
position: absolute;
|
||
right: 80rpx;
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
background: rgba(0,0,0, .5);
|
||
border-radius: 40rpx;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.plv-player-lock__btn {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
}
|
||
|
||
.plv-player-skin-drag-tips {
|
||
position: absolute;
|
||
top: 60rpx;
|
||
justify-content: center;
|
||
align-items: center;
|
||
flex-direction: row;
|
||
/* justify-content: center;
|
||
align-items: center; */
|
||
}
|
||
|
||
.plv-player-longpress-speed {
|
||
position: absolute;
|
||
width: 175rpx;
|
||
padding: 10rpx;
|
||
flex-direction: row;
|
||
justify-content: center;
|
||
align-items: center;
|
||
top: 40rpx;
|
||
border-radius: 15px;
|
||
background: rgba(0,0,0,.8);
|
||
}
|
||
|
||
|
||
.plv-player-longpress-speed__image {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
}
|
||
|
||
.plv-player-longpress-speed__text {
|
||
color: #FFFFFF;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.plv-player-comp-box {
|
||
position: absolute;
|
||
height: 300rpx;
|
||
left: 0;
|
||
top: 0;
|
||
z-index: 0;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
</style>
|