feat: 集成音频播放组件和视频播放器
This commit is contained in:
498
uni_modules/yingbing-video/components/modules/iframe.vue
Normal file
498
uni_modules/yingbing-video/components/modules/iframe.vue
Normal file
@@ -0,0 +1,498 @@
|
||||
<template>
|
||||
<view
|
||||
:class="'iframe' + dataId"
|
||||
class="find_iframe"
|
||||
:data-sandbox="sandbox"
|
||||
:data-allow="allow"
|
||||
:data-allowfullscreen="allowfullscreen"
|
||||
:data-frameborder="frameborder"
|
||||
:data-loadingShow="loadingShow"
|
||||
:data-console="console"
|
||||
:data-iframeStyle="iframeStyleString"
|
||||
:data-iframeClass="iframeClassString"
|
||||
:data-crossOrigin="crossOrigin"
|
||||
:data-setDataName="setDataName"
|
||||
:data-overrideUrlLoadingOptions="overrideUrlLoadingOptionsString"
|
||||
:ready="ready" :change:ready="ComIframe.readyWatcher"
|
||||
:isDestroy="isDestroy" :change:isDestroy="ComIframe.destroyWatcher"
|
||||
:src="src" :change:src="ComIframe.srcWatcher"
|
||||
:sandbox="sandbox" :change:sandbox="ComIframe.sandboxWatcher"
|
||||
:allow="allow" :change:allow="ComIframe.allowWatcher"
|
||||
:allowfullscreen="allowfullscreen" :change:allowfullscreen="ComIframe.allowfullscreenWatcher"
|
||||
:frameborder="frameborder" :change:frameborder="ComIframe.frameborderWatcher"
|
||||
:iframeStyle="iframeStyle" :change:iframeStyle="ComIframe.iframeStyleWatcher"
|
||||
:iframeClass="iframeClass" :change:iframeClass="ComIframe.iframeClassWatcher"
|
||||
:jump="jump" :change:jump="ComIframe.jumpWatcher"
|
||||
:assignUrl="assignUrl" :change:assignUrl="ComIframe.assignUrlWatcher"
|
||||
:loadingStatus="loadingStatus" :change:loadingStatus="ComIframe.loadingWatcher"
|
||||
:evalJS="evalJSString" :change:evalJS="ComIframe.evalJSWatcher"
|
||||
:evalCSS="evalCSSString" :change:evalCSS="ComIframe.evalCSSWatcher"
|
||||
:setData="setDataValue" :change:setData="ComIframe.setDataWatcher"
|
||||
:overrideUrlLoading="overrideUrlLoadingOptionsString" :change:overrideUrlLoading="ComIframe.overrideUrlLoadingWatcher">
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
dataId: {
|
||||
type: String,
|
||||
default () {
|
||||
return new Date().getTime().toString() + Math.round(Math.random() * 10000)
|
||||
}
|
||||
},
|
||||
src: {//链接
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
sandbox: {//沙盒模式 原生属性
|
||||
type: String,
|
||||
default: 'allow-same-origin allow-scripts allow-forms allow-top-navigation-by-user-activation allow-popups allow-modals'
|
||||
},
|
||||
allow: {//允许一些操作 原生属性
|
||||
type: String,
|
||||
// default: 'autoplay; fullscreen; encrypted-media; picture-in-picture'
|
||||
default: ''
|
||||
},
|
||||
allowfullscreen: {//是否允许全屏 原生属性
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
//跨域属性 anonymous-它有一个默认值。它定义了将在不传递凭据信息的情况下发送CORS请求 use-credentials-将发送带有凭据、cookie 和证书的 cross-origin 请求
|
||||
crossOrigin: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
frameborder: {//iframe边框 原生属性
|
||||
type: String,
|
||||
default: '0'
|
||||
},
|
||||
iframeClass: {//iframe样式
|
||||
type: [String, Object],
|
||||
default: ''
|
||||
},
|
||||
iframeStyle: {//iframe样式
|
||||
type: [String, Object],
|
||||
default: ''
|
||||
},
|
||||
loadingShow: {//展示loading
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
console: {//控制console
|
||||
type: String,
|
||||
default: 'log warn error'
|
||||
},
|
||||
overrideUrlLoadingOptions: {//仅支持app
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
effect: '',
|
||||
mode: '',
|
||||
match: '',
|
||||
exclude: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
overrideUrlLoadingOptionsString () {
|
||||
return JSON.stringify(this.overrideUrlLoadingOptions)
|
||||
},
|
||||
iframeStyleString () {
|
||||
return JSON.stringify(this.iframeStyle)
|
||||
},
|
||||
iframeClassString () {
|
||||
return JSON.stringify(this.iframeClass)
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
ready: '',
|
||||
jump: 0,
|
||||
isDestroy: false,
|
||||
evalJSString: '',//js字符串
|
||||
evalCSSString: '',//注入css字符串
|
||||
assignUrl: '',//跳转链接
|
||||
loadingStatus: -1,//加载进度提控制
|
||||
evalJSTask: new Map(),//注入js临时队列表
|
||||
setDataName: '',
|
||||
setDataValue: ''
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.$nextTick(function () {
|
||||
setTimeout(() => {
|
||||
this.ready = this.dataId
|
||||
}, 100)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
//返回上一页
|
||||
back () {
|
||||
this.jump = 0
|
||||
this.$nextTick(function () {
|
||||
this.jump = -1
|
||||
})
|
||||
},
|
||||
//调用内部iframe跳转链接
|
||||
assign (url) {
|
||||
this.assignUrl = url
|
||||
},
|
||||
//显示进度条加载
|
||||
showLoading () {
|
||||
this.loadingStatus = -1
|
||||
this.$nextTick(function () {
|
||||
this.loadingStatus = 1
|
||||
})
|
||||
},
|
||||
//关闭进度条加载
|
||||
hideLoading () {
|
||||
this.loadingStatus = -1
|
||||
this.$nextTick(function () {
|
||||
this.loadingStatus = 0
|
||||
})
|
||||
},
|
||||
//销毁
|
||||
destroy () {
|
||||
this.isDestroy = false
|
||||
this.$nextTick(() => {
|
||||
this.isDestroy = true
|
||||
})
|
||||
},
|
||||
//注入js
|
||||
evalJS (str) {
|
||||
if ( this.evalJSString ) {//如果又正在进行注入的js任务
|
||||
const id = new Date().getTime().toString() + Math.round(Math.random() * 10000)
|
||||
this.evalJSTask.set(id, str)//加入等待队列
|
||||
} else {
|
||||
this.evalJSString = str
|
||||
}
|
||||
},
|
||||
//注入css
|
||||
evalCSS (str) {
|
||||
this.evalCSSString = ''
|
||||
this.$nextTick(() => {
|
||||
this.evalCSSString = str
|
||||
})
|
||||
},
|
||||
setData (key, value) {
|
||||
this.setDataName = key
|
||||
this.setDataValue = ''
|
||||
this.$nextTick(() => {
|
||||
this.setDataValue = value
|
||||
})
|
||||
},
|
||||
loadstart (e) {
|
||||
this.$emit('loadstart', e)
|
||||
},
|
||||
loaded (e) {
|
||||
this.$emit('loaded', e)
|
||||
},
|
||||
callError (e) {
|
||||
this.$emit('error', e)
|
||||
},
|
||||
backError () {
|
||||
this.$emit('backerror')
|
||||
},
|
||||
callDestroy () {
|
||||
this.$emit('destroyed')
|
||||
},
|
||||
overrideUrlLoading (e) {
|
||||
this.$emit('overrideurlloading', e)
|
||||
},
|
||||
message (e) {
|
||||
this.$emit('message', e)
|
||||
},
|
||||
callEvalJS (e) {//注入js回调
|
||||
this.evalJSString = ''
|
||||
if ( this.evalJSTask.size ) {//如果等待队列有值,并且当前没有正在执行的注入js任务
|
||||
const firstEntrie = this.evalJSTask.entries().next().value//获取等待队列中的第一个
|
||||
this.evalJS(firstEntrie[1])//执行任务
|
||||
this.evalJSTask.delete(firstEntrie[0])//删除等待队列
|
||||
}
|
||||
if ( e.status == 'error' ) this.callError(e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- #ifdef APP-VUE || H5 -->
|
||||
<script lang="renderjs" type="module" module="ComIframe">
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
dom: null,
|
||||
iframe: null,
|
||||
loading: null,
|
||||
iframeSrc: '',
|
||||
backTimer: null
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('message', this.messageListener);
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.destoryIframe()
|
||||
},
|
||||
methods: {
|
||||
messageListener (e) {
|
||||
this.callMethod('message', {origin: e.origin, data: e.data})
|
||||
},
|
||||
destoryIframe () {
|
||||
window.removeEventListener('message', this.messageListener)
|
||||
if ( this.loading ) {
|
||||
this.loading.remove()
|
||||
this.loading = null
|
||||
}
|
||||
if ( this.iframe ) {
|
||||
this.iframe.remove()
|
||||
this.iframe = null
|
||||
}
|
||||
this.callMethod('callDestroy')
|
||||
},
|
||||
showLoadingRender () {
|
||||
if ( this.loading && this.getData('loadingShow') ) {
|
||||
this.loading.classList.remove('browser-loading-hide')
|
||||
this.loading.classList.add('browser-loading-show')
|
||||
}
|
||||
},
|
||||
hideLoadingRender () {
|
||||
if ( this.loading && this.getData('loadingShow') ) {
|
||||
this.loading.classList.remove('browser-loading-show')
|
||||
this.loading.classList.add('browser-loading-hide')
|
||||
}
|
||||
},
|
||||
clearBackTimer () {
|
||||
if ( this.backTimer ) {
|
||||
window.clearTimeout(this.backTimer)
|
||||
this.backTimer = null
|
||||
}
|
||||
},
|
||||
initIframe () {
|
||||
this.showLoadingRender()
|
||||
this.callMethod('loadstart', {href: this.iframeSrc})
|
||||
this.iframe = document.createElement('IFRAME')
|
||||
this.iframe.setAttribute('class', 'find_iframe_iframe')
|
||||
this.iframe.setAttribute('frameborder', this.getData('frameborder'))
|
||||
this.iframe.setAttribute('allow', this.getData('allow'))
|
||||
this.iframe.setAttribute('allowfullscreen', this.getData('allowfullscreen'))
|
||||
this.iframe.setAttribute('sandbox', this.getData('sandbox'))
|
||||
this.iframe.setAttribute('crossOrigin', this.getData('crossOrigin'))
|
||||
if ( this.getData('iframeStyle') ) this.iframeStyleWatcher(JSON.parse(this.getData('iframeStyle')))//初始化style
|
||||
if ( this.getData('iframeClass') ) this.iframeClassWatcher(JSON.parse(this.getData('iframeClass')))//初始化style
|
||||
this.iframe.src = this.iframeSrc
|
||||
this.iframe.onload = () => {
|
||||
this.hideLoadingRender()
|
||||
const iframeWindow = this.iframe.contentWindow
|
||||
iframeWindow.onbeforeunload = (e) => {
|
||||
this.clearBackTimer()
|
||||
this.callMethod('loadstart', e)
|
||||
this.showLoadingRender()
|
||||
}
|
||||
const history = iframeWindow.history
|
||||
const iframeDocument = this.iframe.contentDocument || iframeWindow.document;
|
||||
const title = iframeDocument.title
|
||||
const href = iframeWindow.location.href
|
||||
const head = iframeDocument.head;
|
||||
const links = head.querySelectorAll('link[rel="icon"], link[rel="shortcut icon"]');//尝试从网页代码中获取favicon.ico
|
||||
const favicon = links.length > 0 ? links[0].href : href + (href.substring(href.length -1) == '/' ? '' : '/') + 'favicon.ico'//没有获取到默认使用网址拼接favicon.ico
|
||||
this.callMethod('loaded', {title, href, favicon, history})
|
||||
}
|
||||
this.iframe.onerror = (e) => {
|
||||
this.hideLoadingRender()
|
||||
this.callMethod('callError', e)
|
||||
}
|
||||
this.dom.appendChild(this.iframe)
|
||||
//控制iframe内部的console
|
||||
const consoles = this.getData('console').split(' ')
|
||||
Object.keys(this.iframe.contentWindow.console).forEach(key => {
|
||||
if ( consoles.indexOf(key) == -1 ) this.iframe.contentWindow.console[key] = function () {}
|
||||
})
|
||||
const options = JSON.parse(this.getData('overrideUrlLoadingOptions'))
|
||||
if ( options && options.mode ) this.overrideUrlLoadingWatcher(this.getData('overrideUrlLoadingOptions'))
|
||||
},
|
||||
readyWatcher (newVal) {
|
||||
if ( newVal ) {
|
||||
this.dom = document.querySelector('.iframe' + newVal)
|
||||
this.loading = document.createElement('DIV')
|
||||
this.loading.setAttribute('class', 'browser-loading-line')
|
||||
this.dom.appendChild(this.loading)
|
||||
}
|
||||
if ( this.iframeSrc && this.dom ) this.initIframe()
|
||||
},
|
||||
destroyWatcher (newVal) {
|
||||
if ( newVal ) this.destoryIframe()
|
||||
},
|
||||
srcWatcher (newVal, oldVal) {
|
||||
this.iframeSrc = newVal
|
||||
if ( !this.dom ) return
|
||||
if ( newVal != oldVal ) {
|
||||
if ( oldVal ) this.destoryIframe()
|
||||
if ( newVal ) this.initIframe()
|
||||
}
|
||||
},
|
||||
sandboxWatcher (newVal) {
|
||||
this.iframe && this.iframe.setAttribute('sandbox', newVal)
|
||||
},
|
||||
allowWatcher (newVal) {
|
||||
this.iframe && this.iframe.setAttribute('allow', allow)
|
||||
},
|
||||
allowfullscreenWatcher (newVal) {
|
||||
this.iframe && this.iframe.setAttribute('allowfullscreen', newVal)
|
||||
},
|
||||
frameborderWatcher (newVal) {
|
||||
this.iframe && this.iframe.setAttribute('frameborder', newVal)
|
||||
},
|
||||
iframeStyleWatcher (newVal) {
|
||||
if ( typeof newVal == "string" ) this.iframe && this.iframe.setAttribute('style', newVal)
|
||||
if ( typeof newVal == "object" ) {
|
||||
let style = ''
|
||||
Object.keys(newVal).forEach(key => {
|
||||
style += key + ':' + newVal[key] + ';'
|
||||
})
|
||||
this.iframe && this.iframe.setAttribute('style', style)
|
||||
}
|
||||
},
|
||||
iframeClassWatcher (newVal) {
|
||||
if ( typeof newVal == "string" ) this.iframe && this.iframe.setAttribute('class', 'find_iframe_iframe ' + newVal)
|
||||
if ( typeof newVal == "object" ) {
|
||||
let className = ''
|
||||
Object.keys(newVal).forEach(key => {
|
||||
className += newVal[key] ? (key + ' ') : ''
|
||||
})
|
||||
this.iframe && this.iframe.setAttribute('class', 'find_iframe_iframe ' + className)
|
||||
}
|
||||
},
|
||||
jumpWatcher (newVal) {
|
||||
if ( newVal < 0 && this.iframe ) {
|
||||
this.iframe.contentWindow.history.back()
|
||||
this.backTimer = window.setTimeout(() => {
|
||||
this.callMethod('backError')
|
||||
}, 300)
|
||||
}
|
||||
},
|
||||
assignUrlWatcher (newVal) {
|
||||
const iframeWindow = this.iframe?.contentWindow
|
||||
iframeWindow && iframeWindow.window.location.assign(newVal);
|
||||
},
|
||||
loadingWatcher (newVal) {
|
||||
if ( newVal == 0 ) this.hideLoading()
|
||||
if ( newVal == 1 ) this.showLoading()
|
||||
},
|
||||
evalJSWatcher (newVal) {
|
||||
if ( newVal ) {
|
||||
try{
|
||||
const iframeDocument = this.iframe.contentDocument || iframeWindow.document;
|
||||
const script = iframeDocument.createElement('SCRIPT')
|
||||
script.innerHTML = newVal
|
||||
iframeDocument.head.appendChild(script)//插入script标签
|
||||
this.callMethod('callEvalJS', {status: 'success'})//通知逻辑层成功执行回调
|
||||
iframeDocument.head.removeChild(script)//执行完script标签后需要移除
|
||||
}catch(e){
|
||||
this.callMethod('callEvalJS', {status: 'error', message: JSON.stringify(e)})
|
||||
}
|
||||
}
|
||||
},
|
||||
evalCSSWatcher (newVal) {
|
||||
if ( newVal ) {
|
||||
try{
|
||||
const iframeDocument = this.iframe.contentDocument || iframeWindow.document;
|
||||
const style = iframeDocument.createElement('STYLE')
|
||||
style.innerHTML = newVal
|
||||
iframeDocument.head.appendChild(style)
|
||||
}catch(e){
|
||||
this.callMethod('callError', e)
|
||||
}
|
||||
}
|
||||
},
|
||||
//给网页值传参
|
||||
setDataWatcher (newVal) {
|
||||
if ( newVal ) {
|
||||
try{
|
||||
const iframeWindow = this.iframe.contentWindow
|
||||
const key = this.getData('setDataName')
|
||||
if ( key ) iframeWindow[key] = newVal
|
||||
}catch(e){
|
||||
this.callMethod('callEvalJS', {status: 'error', message: JSON.stringify(e)})
|
||||
}
|
||||
}
|
||||
},
|
||||
//拦截监听
|
||||
overrideUrlLoadingWatcher (newVal) {
|
||||
// #ifdef APP-VUE
|
||||
const wv = plus.webview.currentWebview()
|
||||
const options = JSON.parse(newVal)
|
||||
wv.overrideUrlLoading(options, (e) => {
|
||||
this.callMethod('overrideUrlLoading', e)
|
||||
return true;
|
||||
});
|
||||
// #endif
|
||||
|
||||
},
|
||||
getData (name) {
|
||||
const value = this.dom.getAttribute('data-' + name)
|
||||
if ( ['true', 'false'].includes(value) ) return value == 'false' ? false : true
|
||||
else if ( /^\d+$/.test(value) ) return Number(value)
|
||||
else return value
|
||||
},
|
||||
callMethod (name, args) {
|
||||
// #ifndef H5
|
||||
this.$ownerInstance.callMethod(name, args)
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
this[name](args)
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
|
||||
|
||||
<style>
|
||||
@keyframes loading-show {
|
||||
from {
|
||||
width: 0;
|
||||
}
|
||||
to {
|
||||
width: 99%;
|
||||
}
|
||||
}
|
||||
@keyframes loading-hide {
|
||||
from {
|
||||
width: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
width: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.find_iframe {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
/deep/ .find_iframe_iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
/deep/ .browser-loading-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 3px;
|
||||
border-radius: 3px;
|
||||
background-color: #4cd964;
|
||||
z-index: 1;
|
||||
touch-action: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
/deep/ .browser-loading-show {
|
||||
animation: loading-show 5s ease both;
|
||||
}
|
||||
/deep/ .browser-loading-hide {
|
||||
animation: loading-hide .5s linear both;
|
||||
}
|
||||
</style>
|
||||
648
uni_modules/yingbing-video/components/modules/video.vue
Normal file
648
uni_modules/yingbing-video/components/modules/video.vue
Normal file
@@ -0,0 +1,648 @@
|
||||
<template>
|
||||
<view class="render-video" :class="'rvideo' + dataId"
|
||||
:data-route="pageRoute"
|
||||
:dataId="dataId" :change:dataId="RenderVideo.dataIdWatcher"
|
||||
:videoParams="videoParams" :change:videoParams="RenderVideo.reloadVideoRender"
|
||||
:isUnload="isUnload" :change:isUnload="RenderVideo.destroyRender"
|
||||
:loadDanmuParams="loadDanmuParams" :change:loadDanmuParams="RenderVideo.loadDanmuRender"
|
||||
:isUnloadDanmu="isUnloadDanmu" :change:isUnloadDanmu="RenderVideo.unloadDanmuRender"
|
||||
:sendDanmuParams="sendDanmuParams" :change:sendDanmuParams="RenderVideo.sendDanmuRender"
|
||||
:insertDanmuParams="insertDanmuParams" :change:insertDanmuParams="RenderVideo.insertDanmuRender"
|
||||
:setVideoParams="setVideoParams" :change:setVideoParams="RenderVideo.setVideoRender"
|
||||
:reloadCustomParams="reloadCustomParams" :change:reloadCustomParams="RenderVideo.reloadCustomRender"
|
||||
:updateConfigParams="updateConfigParams" :change:updateConfigParams="RenderVideo.updateConfigRender"
|
||||
:isPlay="isPlay" :change:isPlay="RenderVideo.playRender"
|
||||
:isPause="isPause" :change:isPause="RenderVideo.pauseRender"
|
||||
:isToggle="isToggle" :change:isToggle="RenderVideo.toggleRender"
|
||||
:seekTime="seekTime" :change:seekTime="RenderVideo.seekRender"
|
||||
:isOpenFullscreen="isOpenFullscreen" :change:isOpenFullscreen="RenderVideo.openFullscreenRender"
|
||||
:isExitFullscreen="isExitFullscreen" :change:isExitFullscreen="RenderVideo.exitFullscreenRender"
|
||||
:captureParams="captureParams" :change:captureParams="RenderVideo.captureRender"
|
||||
:toastParams="toastParams" :change:toastParams="RenderVideo.showToastRender"
|
||||
:toolbarParams="toolbarParams" :change:toolbarParams="RenderVideo.showToolbarRender"
|
||||
:isEnableGesture="isEnableGesture" :change:isEnableGesture="RenderVideo.enableGestureRender"
|
||||
:isDisableGesture="isDisableGesture" :change:isDisableGesture="RenderVideo.disableGestureRender"
|
||||
:isUpdateSize="isUpdateSize" :change:isUpdateSize="RenderVideo.updateSizeRender">
|
||||
<view :class="'rvideoslot' + dataId">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
dataId: '',
|
||||
pageRoute: '',//当前页面的路由名称
|
||||
videoParams: '',//视频参数
|
||||
isUnload: -1,//是否卸载视频
|
||||
loadDanmuParams: '',//弹幕配置
|
||||
isUnloadDanmu: -1,//是否卸载弹幕
|
||||
sendDanmuParams: '',//发送弹幕
|
||||
insertDanmuParams: '',//插入弹幕
|
||||
setVideoParams: '',//设置video属性
|
||||
reloadCustomParams: '',//重加载自定义配置
|
||||
updateConfigParams: '',//更新配置
|
||||
isPlay: -1,//是否播放
|
||||
isPause: -1,//是否暂停
|
||||
isToggle: -1,//是否切换
|
||||
seekTime: -1,//跳转时间
|
||||
isOpenFullscreen: -1,//是否开启全屏
|
||||
isExitFullscreen: -1,//是否退出全屏
|
||||
captureParams: '',//截屏
|
||||
toastParams: '',//消息提示
|
||||
toolbarParams: '',//工具栏
|
||||
isEnableGesture: -1,//是否开启手势事件
|
||||
isDisableGesture: -1,//是否关闭手势事件
|
||||
isUpdateSize: -1//是否更新画布尺寸
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.dataId = new Date().getTime().toString() + Math.round(Math.random() * 10000)
|
||||
const pages = getCurrentPages()
|
||||
const page = pages[pages.length-1]
|
||||
this.pageRoute = page.route
|
||||
},
|
||||
methods: {
|
||||
ready () {
|
||||
this.emit('message', {ready: true})
|
||||
},
|
||||
destroyed () {
|
||||
this.$emit('destroyed')
|
||||
},
|
||||
message (e) {
|
||||
this.$emit('message', e)
|
||||
},
|
||||
emit (name, data) {
|
||||
this.$emit(name, {
|
||||
data
|
||||
})
|
||||
},
|
||||
//重加载视频
|
||||
reloadVideo (params) {
|
||||
this.videoParams = ''
|
||||
this.$nextTick(() => {
|
||||
this.videoParams = params
|
||||
})
|
||||
},
|
||||
//卸载视频
|
||||
destroy () {
|
||||
this.isUnload = -1
|
||||
this.$nextTick(() => {
|
||||
this.isUnload = 1
|
||||
})
|
||||
},
|
||||
//加载弹幕
|
||||
loadDanmu (danmu) {
|
||||
this.loadDanmuParams = ''
|
||||
this.$nextTick(() => {
|
||||
this.loadDanmuParams = danmu
|
||||
})
|
||||
},
|
||||
//卸载弹幕
|
||||
unloadDanmu () {
|
||||
this.isUnloadDanmu = -1
|
||||
this.$nextTick(() => {
|
||||
this.isUnloadDanmu = 1
|
||||
})
|
||||
},
|
||||
//发送弹幕
|
||||
sendDanmu (danmu, border) {
|
||||
this.sendDanmuParams = ''
|
||||
this.$nextTick(() => {
|
||||
this.sendDanmuParams = {
|
||||
danmu,
|
||||
border
|
||||
}
|
||||
})
|
||||
},
|
||||
//插入弹幕
|
||||
insertDanmu (danmu) {
|
||||
this.insertDanmuParams = ''
|
||||
this.$nextTick(() => {
|
||||
this.insertDanmuParams = danmu
|
||||
})
|
||||
},
|
||||
//动态修改video属性
|
||||
setVideo (key, value) {
|
||||
this.setVideoParams = ''
|
||||
this.$nextTick(() => {
|
||||
this.setVideoParams = {
|
||||
key, value
|
||||
}
|
||||
})
|
||||
},
|
||||
//重加载自定义配置
|
||||
reloadCustom (params) {
|
||||
this.reloadCustomParams = ''
|
||||
this.$nextTick(() => {
|
||||
this.reloadCustomParams = params
|
||||
})
|
||||
},
|
||||
//更新配置
|
||||
updateConfig (params) {
|
||||
this.updateConfigParams = ''
|
||||
this.$nextTick(() => {
|
||||
this.updateConfigParams = params
|
||||
})
|
||||
},
|
||||
//播放视频
|
||||
play () {
|
||||
this.isPlay = -1
|
||||
this.$nextTick(() => {
|
||||
this.isPlay = 1
|
||||
})
|
||||
},
|
||||
//暂停视频
|
||||
pause () {
|
||||
this.isPause = -1
|
||||
this.$nextTick(() => {
|
||||
this.isPause = 1
|
||||
})
|
||||
},
|
||||
//播放和暂停视频
|
||||
toggle () {
|
||||
this.isToggle = -1
|
||||
this.$nextTick(() => {
|
||||
this.isToggle = 1
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 跳转视频
|
||||
* @param {Number} time 跳转位置(单位秒)
|
||||
*/
|
||||
seek (time) {
|
||||
this.seekTime = -1
|
||||
this.$nextTick(() => {
|
||||
this.seekTime = time
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 播放和暂停视频
|
||||
* @param {String} direction 屏幕方向 auto-自动计算 landscape-横屏 portrait-竖屏
|
||||
*/
|
||||
openFullscreen (direction) {
|
||||
this.isOpenFullscreen = -1
|
||||
this.$nextTick(() => {
|
||||
this.isOpenFullscreen = direction
|
||||
})
|
||||
},
|
||||
//退出全屏
|
||||
exitFullscreen () {
|
||||
this.isExitFullscreen = -1
|
||||
this.$nextTick(() => {
|
||||
this.isExitFullscreen = 1
|
||||
})
|
||||
},
|
||||
//截图
|
||||
capture (data) {
|
||||
this.captureParams = ''
|
||||
this.$nextTick(() => {
|
||||
this.captureParams = data
|
||||
})
|
||||
},
|
||||
//消息提示
|
||||
showToast (data) {
|
||||
this.toastParams = ''
|
||||
this.$nextTick(() => {
|
||||
this.toastParams = data
|
||||
})
|
||||
},
|
||||
//展示工具栏
|
||||
showToolbar (data) {
|
||||
this.toolbarParams = ''
|
||||
this.$nextTick(() => {
|
||||
this.toolbarParams = data
|
||||
})
|
||||
},
|
||||
//禁用手势事件
|
||||
disableGesture () {
|
||||
this.isDisableGesture = -1
|
||||
this.$nextTick(() => {
|
||||
this.isDisableGesture = 1
|
||||
})
|
||||
},
|
||||
//启用手势事件
|
||||
enableGesture () {
|
||||
this.isEnableGesture = -1
|
||||
this.$nextTick(() => {
|
||||
this.isEnableGesture = 1
|
||||
})
|
||||
},
|
||||
updateSize () {
|
||||
this.isUpdateSize = -1
|
||||
this.$nextTick(() => {
|
||||
this.isUpdateSize = 1
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script lang="renderjs" module="RenderVideo" type="module">
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
dom: null,
|
||||
domSlot: null,
|
||||
mp: null
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.destroyRender()
|
||||
},
|
||||
methods: {
|
||||
dataIdWatcher (newVal) {
|
||||
if ( newVal ) {
|
||||
this.dom = document.querySelector('.rvideo' + newVal)
|
||||
this.domSlot = document.querySelector('.rvideoslot' + newVal)
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
async init () {
|
||||
if ( !window.Hls ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/js/hls.min.js'), 'hls')
|
||||
if ( !window.flvjs ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/js/flv.min.js'), 'flv')
|
||||
if ( !window.JSMpeg ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/js/jsmpeg.min.js'), 'JSMpeg')
|
||||
if ( !window.THREE ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/js/three.min.js'), 'three')
|
||||
if ( !window.THREE.OrbitControls ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/js/OrbitControls.js'), 'OrbitControls')
|
||||
if ( !window.THREE.DeviceOrientationControls ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/js/DeviceOrientationControls.js'), 'DeviceOrientationControls')
|
||||
if ( !window.YbSubtitle ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/dist/yb-player-subtitle.js'), 'ybPlayerSubtitle')
|
||||
if ( !window.YbDanmu ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/dist/yb-player-danmu.js'), 'ybPlayerDanmu')
|
||||
if ( !window.YbPano ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/dist/yb-player-pano.js'), 'ybPlayerPano')
|
||||
if ( !window.YbMpeg ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/dist/yb-player-mpeg.js'), 'ybPlayerMpeg')
|
||||
if ( !window.YbPlayer ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/dist/yb-player.js'), 'ybPlayer')
|
||||
if ( !window.YbGesture ) await this.loadScriptInSandbox(this.parseSrc('/uni_modules/yingbing-video/static/html/dist/yb-player-gesture.js'), 'ybPlayerGesture')
|
||||
this.loadCss()
|
||||
this.callMethod('ready')
|
||||
},
|
||||
loadCss () {
|
||||
let links = document.getElementsByTagName('link')
|
||||
let linkarr = []
|
||||
for ( let i = 0; i < links.length; i++ ) {
|
||||
if ( links[i].getAttribute('data-id') ) linkarr.push(links[i].getAttribute('data-id'))
|
||||
}
|
||||
//判断一下css文件是否已加载,避免重复加载
|
||||
if ( linkarr.indexOf('yb-player') == -1 ) {
|
||||
const link = document.createElement('LINK')
|
||||
link.setAttribute('data-id', 'yb-player')
|
||||
link.rel = 'stylesheet'
|
||||
link.href = this.parseSrc('/uni_modules/yingbing-video/static/html/css/yb-player.css')
|
||||
document.head.appendChild(link)
|
||||
}
|
||||
//判断一下css文件是否已加载,避免重复加载
|
||||
if ( linkarr.indexOf('yb-player-plugin') == -1 ) {
|
||||
const link = document.createElement('LINK')
|
||||
link.setAttribute('data-id', 'yb-player-plugin')
|
||||
link.rel = 'stylesheet'
|
||||
link.href = this.parseSrc('/uni_modules/yingbing-video/static/html/css/yb-player-plugin.css')
|
||||
document.head.appendChild(link)
|
||||
}
|
||||
},
|
||||
request (url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
//如果是在线链接表示当前运行为H5,直接用XMLHttpRequest请求
|
||||
if (url.startsWith('http') ) {
|
||||
// #ifdef H5
|
||||
var xhr = new XMLHttpRequest()
|
||||
// #endif
|
||||
// #ifdef APP-VUE
|
||||
var xhr = new plus.net.XMLHttpRequest()
|
||||
// #endif
|
||||
xhr.onreadystatechange = () => {
|
||||
if ( xhr.readyState == 4 ) {
|
||||
if ( xhr.status == 200 ) {
|
||||
resolve(xhr.responseText)
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
xhr.abort()
|
||||
}
|
||||
}
|
||||
xhr.onabort = function () { xhr = null }
|
||||
xhr.open('GET', url);
|
||||
xhr.responseType = 'text';
|
||||
xhr.send();
|
||||
} else {
|
||||
//非http协议链接,需要使用系统文件API,获取文件内容,兼容IOS得WkWbview内核
|
||||
// #ifdef APP-VUE
|
||||
//将路径开头替换为_www/,这是为了兼容ios文件路径读取
|
||||
plus.io.resolveLocalFileSystemURL(url.replace('./', '_www/'), function( entry ) {
|
||||
entry.file( function(file){
|
||||
var fileReader = new plus.io.FileReader();
|
||||
fileReader.readAsText(file, 'utf-8');
|
||||
fileReader.onloadend = function(evt) {
|
||||
resolve(evt.target.result)
|
||||
}
|
||||
} );
|
||||
}, function ( e ) {
|
||||
resolve(null)
|
||||
} );
|
||||
// #endif
|
||||
// #ifndef APP-VUE
|
||||
console.log('不支持本地文件访问')
|
||||
// #endif
|
||||
}
|
||||
|
||||
})
|
||||
},
|
||||
parseSrc (path) {
|
||||
// #ifdef H5
|
||||
const isHash = window.location.hash
|
||||
const route = this.dom.getAttribute('data-route')
|
||||
const pathName = isHash ? window.location.pathname : window.location.pathname.replace(route, '')
|
||||
return window.location.origin + pathName + path.substring(1)
|
||||
// #endif
|
||||
// #ifdef APP-VUE
|
||||
return '.' + path
|
||||
// #endif
|
||||
},
|
||||
// 安全加载JS库的方法
|
||||
async loadScriptInSandbox (url, id) {
|
||||
try {
|
||||
const response = await this.request(url)
|
||||
const jsCode = response
|
||||
|
||||
// const uni = undefined
|
||||
// eval('(function () {' + jsCode + '}())')
|
||||
|
||||
//创建隔离的执行环境 似乎没有效果
|
||||
const sandbox = {
|
||||
window: window,
|
||||
document: document,
|
||||
navigator: navigator
|
||||
// 添加其他必要的全局对象...
|
||||
};
|
||||
const code = `
|
||||
(function(sandbox) {
|
||||
var window = sandbox.window;
|
||||
var document = sandbox.document;
|
||||
var navigator = sandbox.navigator;
|
||||
var uni = undefined;
|
||||
${jsCode}
|
||||
})(sandbox);
|
||||
`;
|
||||
// 使用Function构造器创建隔离的执行环境
|
||||
const executeInSandbox = new Function('sandbox',code);
|
||||
// 执行代码
|
||||
executeInSandbox(sandbox);
|
||||
return { success: true, sandbox };
|
||||
} catch (error) {
|
||||
console.error(`加载脚本失败: ${error}`);
|
||||
// return { success: false, error };
|
||||
}
|
||||
},
|
||||
async loadScriptInDom (src, id) {
|
||||
return new Promise(resolve => {
|
||||
const script = document.createElement('SCRIPT')
|
||||
script.setAttribute('data-id', id)
|
||||
script.src = src
|
||||
script.onload = function () {
|
||||
resolve(true)
|
||||
}
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
//深度克隆数据,避免数据污染
|
||||
_traverseObject (obj, emitname) {
|
||||
if(typeof obj !== "object" && typeof obj !== 'function') {
|
||||
//原始类型直接返回
|
||||
return obj;
|
||||
}
|
||||
var o = Object.prototype.toString.call(obj) === '[object Array]' ? [] : {};
|
||||
for(let i in obj) {
|
||||
if(obj.hasOwnProperty(i)){
|
||||
const value = obj[i]
|
||||
const emit = {}
|
||||
emit[emitname] = value
|
||||
o[i] = i == 'click' ? () => { this.callMethod('message', {data: emit}) } : typeof value === 'object' ? this._traverseObject(value, emitname) : value
|
||||
}
|
||||
}
|
||||
return o;
|
||||
},
|
||||
//处理hlsConfig
|
||||
parseHlsConfig (config = {}) {
|
||||
Object.keys(config).forEach(key => {
|
||||
if ( ['xhrSetup'].includes(key) ) {
|
||||
config[key] = new Function('return (' + config[key] + ')')();
|
||||
}
|
||||
if ( ['pLoader', 'fLoader'].includes(key) ) {
|
||||
config[key] = (new Function(`return ${config[key]}`))();
|
||||
}
|
||||
})
|
||||
return config
|
||||
},
|
||||
reloadVideoRender (params) {
|
||||
if ( !params ) return
|
||||
this.unloadRender()
|
||||
const custom = this._traverseObject(params.custom, 'slotclick')
|
||||
this.mp = new YbPlayer({
|
||||
container: this.dom,
|
||||
src: params.src,
|
||||
segments: params.segments,
|
||||
title: params.title,
|
||||
poster: params.poster || undefined,
|
||||
type: params.type,
|
||||
three: params.three,
|
||||
initialTime: params.initialTime,
|
||||
duration: params.duration,
|
||||
autoplay: params.autoplay,
|
||||
preload: params.preload,
|
||||
muted: params.muted,
|
||||
playbackRate: params.playbackRate,
|
||||
loop: params.loop,
|
||||
isLive: params.isLive,
|
||||
header: params.header,
|
||||
controls: params.controls,
|
||||
height: '100%',
|
||||
objectFit: params.objectFit,
|
||||
crossOrigin: params.crossOrigin,
|
||||
openDirection: params.openDirection,
|
||||
exitDirection: params.exitDirection,
|
||||
quality: params.quality,
|
||||
works: params.works,
|
||||
workIndex: params.workIndex,
|
||||
subtitles: params.subtitles,
|
||||
subtitleIndex: params.subtitleIndex,
|
||||
custom,
|
||||
decoder: {
|
||||
hls: {
|
||||
loader: Hls,
|
||||
config: this.parseHlsConfig(params.hlsConfig)
|
||||
},
|
||||
flv: {
|
||||
loader: flvjs,
|
||||
config: params.flvConfig
|
||||
},
|
||||
jsmpeg: {
|
||||
loader: JSMpeg,
|
||||
config: params.jsmpegConfig
|
||||
}
|
||||
}
|
||||
})
|
||||
this.mp.load()
|
||||
this.mp.loadVideo()
|
||||
this.mp.loadGestureEvent()
|
||||
this.mp.appendDom(this.domSlot)
|
||||
this.mp.onmessage = (data) => {
|
||||
this.callMethod('message', {data})
|
||||
}
|
||||
window.addEventListener('resize', this.updateSizeRender)
|
||||
},
|
||||
//动态修改video属性
|
||||
setVideoRender (newVal) {
|
||||
if ( !newVal ) return
|
||||
const { key, value } = newVal
|
||||
this.mp?.setVideo(key, value)
|
||||
},
|
||||
//加载弹幕
|
||||
loadDanmuRender (danmu) {
|
||||
if ( !danmu ) return
|
||||
if ( this.mp ) {
|
||||
this.mp.setConfig('danmu', YbPlayer.deepClone(danmu))
|
||||
this.mp.unloadDanmu()
|
||||
this.mp.loadDanmu()
|
||||
}
|
||||
},
|
||||
//卸载弹幕
|
||||
unloadDanmuRender (newVal) {
|
||||
if ( newVal == -1 ) return
|
||||
this.mp?.unloadDanmu()
|
||||
},
|
||||
//发送弹幕
|
||||
sendDanmuRender (newVal) {
|
||||
if ( !newVal ) return
|
||||
const { danmu, border } = newVal
|
||||
this.mp?.sendDanmu(danmu, border)
|
||||
},
|
||||
//插入弹幕
|
||||
insertDanmuRender (newVal) {
|
||||
if ( !newVal ) return
|
||||
this.mp?.insertDanmu(newVal)
|
||||
},
|
||||
//更新配置
|
||||
updateConfigRender (config) {
|
||||
if ( !config ) return
|
||||
Object.keys(config).forEach(key => {
|
||||
this.mp?.setConfig(key, config[key])
|
||||
})
|
||||
this.mp?.hideControls()
|
||||
},
|
||||
//重加载自定义配置
|
||||
reloadCustomRender (config) {
|
||||
if ( !config ) return
|
||||
const newConfig = this._traverseObject(config, 'slotclick')
|
||||
Object.keys(newConfig).forEach(key => {
|
||||
this.mp?.setCustom(key, newConfig[key])
|
||||
})
|
||||
this.mp?.unloadCustom()
|
||||
this.mp?.loadCustom()
|
||||
},
|
||||
//播放/暂停
|
||||
toggleRender (newVal) {
|
||||
if ( newVal == -1 ) return
|
||||
this.mp?.toggle()
|
||||
},
|
||||
//播放
|
||||
playRender (newVal) {
|
||||
if ( newVal == -1 ) return
|
||||
this.mp?.video?.play()
|
||||
},
|
||||
//暂停
|
||||
pauseRender (newVal) {
|
||||
if ( newVal == -1 ) return
|
||||
this.mp?.video?.pause()
|
||||
},
|
||||
//跳转
|
||||
seekRender (time) {
|
||||
if ( time == -1 ) return
|
||||
this.mp?.seek(time)
|
||||
},
|
||||
//开启全屏
|
||||
openFullscreenRender (direction) {
|
||||
if ( direction == -1 ) return
|
||||
this.mp?.openFullscreen(direction)
|
||||
},
|
||||
//退出全屏
|
||||
exitFullscreenRender (newVal) {
|
||||
if ( newVal == -1 ) return
|
||||
this.mp?.exitFullscreen()
|
||||
},
|
||||
//消息提示
|
||||
showToastRender (data) {
|
||||
if ( !data ) return
|
||||
this.mp?.showToast(data)
|
||||
},
|
||||
//展示工具栏
|
||||
showToolbarRender (data) {
|
||||
if ( !data ) return
|
||||
const newDate = this._traverseObject(data, 'toolclick')
|
||||
this.mp?.showToolbar(newDate.selector, newDate.list, newDate.checkShow, newDate.checkIndex)
|
||||
},
|
||||
//截图
|
||||
captureRender (data) {
|
||||
if ( !data ) return
|
||||
this.mp?.capture(data.type, data.show)
|
||||
},
|
||||
//禁用手势事件
|
||||
disableGestureRender (newVal) {
|
||||
if ( newVal == -1 ) return
|
||||
this.mp?.disableGesture()
|
||||
},
|
||||
//启用手势事件
|
||||
enableGestureRender (newVal) {
|
||||
if ( newVal == -1 ) return
|
||||
this.mp?.enableGesture()
|
||||
},
|
||||
//卸载视频
|
||||
unloadRender () {
|
||||
if ( this.mp ) {
|
||||
if ( this.domSlot && this.dom ) this.dom.appendChild(this.domSlot)
|
||||
this.mp.unloadDanmu()
|
||||
this.mp.unloadGestureEvent()
|
||||
this.mp.unloadVideo()
|
||||
this.mp.unload()
|
||||
window.removeEventListener('resize', this.updateSizeRender)
|
||||
}
|
||||
},
|
||||
//卸载视频
|
||||
destroyRender (newVal) {
|
||||
if ( newVal == 1 ){
|
||||
this.unloadRender()
|
||||
this.callMethod('destroyed')
|
||||
}
|
||||
},
|
||||
//重置画布尺寸
|
||||
updateSizeRender () {
|
||||
//重置弹幕画布
|
||||
this.mp?.refreshDanmu()
|
||||
//重置3D画布
|
||||
this.mp?.refreshPano()
|
||||
},
|
||||
callMethod (name, args) {
|
||||
// #ifndef H5
|
||||
this.$ownerInstance && this.$ownerInstance.callMethod(name, args)
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
this[name] && this[name](args)
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.render-video {
|
||||
background-color: #000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.render-video-slot {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
828
uni_modules/yingbing-video/components/yb-video/yb-video.vue
Normal file
828
uni_modules/yingbing-video/components/yb-video/yb-video.vue
Normal file
@@ -0,0 +1,828 @@
|
||||
<template>
|
||||
<view class="yingbing-video" :class="{'yingbing-video-full': fullscreen}" :style="[boxStyle]">
|
||||
<!-- #ifdef H5 || APP-VUE -->
|
||||
<!-- 根据平台选择加载方式因为IOS使用WKWebview内核时严格执行同源策略,对于iframe加载本地网页以及非同源线上网页都有很大的限制,无法操作iframe内容 -->
|
||||
<r-video v-if="renderType == 'renderjs'" ref="web" class="yb-iframe" @message="handleMessage" @destroyed='handleDestroy'>
|
||||
<slot></slot>
|
||||
</r-video>
|
||||
<r-iframe v-else ref="web" class="yb-iframe"
|
||||
crossOrigin="anonymous"
|
||||
:src="webSrc" @message="handleMessage" :loading-show="false" @destroyed='handleDestroy'></r-iframe>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<web-view style="flex: 1;" ref="web" :src="webSrc" @onPostMessage="handleMessage"></web-view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef APP-VUE || H5
|
||||
import RIframe from '../modules/iframe.vue'
|
||||
import RVideo from '../modules/video.vue'
|
||||
// #endif
|
||||
export default {
|
||||
// #ifdef APP-VUE || H5
|
||||
components: {RIframe, RVideo},
|
||||
// #endif
|
||||
props: {
|
||||
//iframe或者webview访问的自定义src
|
||||
webSrc: {
|
||||
type: String,
|
||||
default: () => {
|
||||
const path = '/uni_modules/yingbing-video/static/html/video.html'
|
||||
// #ifdef H5
|
||||
const isHash = window.location.hash
|
||||
const pages = getCurrentPages()
|
||||
const page = pages[pages.length-1]
|
||||
const pathName = isHash ? window.location.pathname : window.location.pathname.replace(page.route, '')
|
||||
return window.location.origin + pathName + path.substring(1)
|
||||
// #endif
|
||||
// #ifdef APP-VUE
|
||||
return '.' + path
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
return path
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
//播放链接
|
||||
src: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//切片列表
|
||||
segments: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
//封面
|
||||
poster: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//解码类型 auto-自动判断 hls-使用hls.js加载 flv-使用flv.js加载 video-使用video加载
|
||||
type: {
|
||||
type: String,
|
||||
default: 'auto'
|
||||
},
|
||||
//初始化时间
|
||||
initialTime: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
//自定义最大播放时长
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
//3D模式 none-关闭3D 360-360全景
|
||||
three: {
|
||||
type: String,
|
||||
default: 'none'
|
||||
},
|
||||
//自动播放(不保证能执行)
|
||||
autoplay: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
//预加载 auto-浏览器尽可能地预加载整个视频文件 metadata-仅预加载视频的元数据 none-不预加载视频
|
||||
preload: {
|
||||
type: String,
|
||||
default: 'auto'
|
||||
},
|
||||
//静音
|
||||
muted: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
//播放倍速
|
||||
playbackRate: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
//循环播放
|
||||
loop: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
//直播流
|
||||
isLive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
//非全屏时是否显示头部控制栏
|
||||
header: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
//是否显示底部控制栏
|
||||
controls: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
//视频组件高度 auto-自适应高度 百分比-根据屏幕高度计算 px/rpx-像素高度
|
||||
height: {
|
||||
type: String,
|
||||
default: 'auto'
|
||||
},
|
||||
//当视频宽高超出容器时的表现形式 fill-内容拉伸填充 contain-保持比例内容缩放 cover-保持比例内容可能被剪切 none-内容不重置 scale-down-保持比例从none或contain选一个 initial-默认值
|
||||
objectFit: {
|
||||
type: String,
|
||||
default: 'contain'
|
||||
},
|
||||
//跨域属性 anonymous-它有一个默认值。它定义了将在不传递凭据信息的情况下发送CORS请求 use-credentials-将发送带有凭据、cookie 和证书的 cross-origin 请求
|
||||
crossOrigin: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//开启全屏时的屏幕方向
|
||||
openDirection: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//退出全屏时的屏幕方向
|
||||
exitDirection: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//弹幕列表
|
||||
danmu: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
//画质列表
|
||||
quality: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
//分p列表
|
||||
works: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
//分P默认索引
|
||||
workIndex: {
|
||||
type: Number,
|
||||
default: -1
|
||||
},
|
||||
//字幕列表
|
||||
subtitles: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
//字幕默认索引
|
||||
subtitleIndex: {
|
||||
type: Number,
|
||||
default: -1
|
||||
},
|
||||
//自定义配置
|
||||
custom: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
//hls配置
|
||||
hlsConfig: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
//flv配置
|
||||
flvConfig: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
//jsmpeg配置
|
||||
jsmpegConfig: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
//渲染类型 APP-NVUE只能使用web网页加载得方式渲染
|
||||
// #ifdef APP-NVUE
|
||||
renderType: {
|
||||
type: String,
|
||||
default: 'web'
|
||||
},
|
||||
// #endif
|
||||
//渲染类型
|
||||
// #ifdef APP-VUE || H5
|
||||
renderType: {
|
||||
type: String,
|
||||
default: () => {
|
||||
//如果是ios平台,默认使用renderjs方式渲染
|
||||
if ( uni.getSystemInfoSync().platform == 'ios' ) {
|
||||
return 'renderjs'
|
||||
} else {
|
||||
return 'web'
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
computed: {
|
||||
boxStyle () {
|
||||
return this.height == 'inherit' ? {
|
||||
height: this.boxHeight,
|
||||
flex: 1
|
||||
} : {
|
||||
height: this.boxHeight
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
ready: false,
|
||||
boxHeight: '',
|
||||
videoWidth: 0,
|
||||
videoHeight: 0,
|
||||
fullscreen: false,
|
||||
customHandles: {},//自定义处理方法
|
||||
slotHandles: {},//存储插槽中的点击函数
|
||||
toolHandles: {}//存储工具栏中的点击函数
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this._removeBackbuttonListener()
|
||||
},
|
||||
created() {
|
||||
//获取当前运行的平台
|
||||
this.updateHeight()
|
||||
},
|
||||
methods: {
|
||||
//接收消息
|
||||
handleMessage (e) {
|
||||
// #ifdef APP-NVUE
|
||||
e.detail.data.forEach(item => {
|
||||
this.parseMessage(item)
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5 || APP-VUE
|
||||
//使用uni.postMessage在h5传递消息,数据结构有很大不同需要找到对应数据
|
||||
this.parseMessage(e.data.type == 'WEB_INVOKE_APPSERVICE' ? e.data.data.arg : e.data)
|
||||
// #endif
|
||||
},
|
||||
//处理返回的消息
|
||||
async parseMessage (data) {
|
||||
//加载视频
|
||||
if ( data.ready ) {
|
||||
this.ready = true
|
||||
if ( this.src || (this.segments && this.segments.length) ) {
|
||||
this.reloadVideo()
|
||||
}
|
||||
}
|
||||
//视频销毁
|
||||
if ( data.destroyed ) {
|
||||
if ( this.renderType != 'renderjs' ) this.$refs.web && this.$refs.web.destroy && this.$refs.web.destroy()
|
||||
}
|
||||
//记录视频尺寸,更新视频高度
|
||||
if ( data.loadeddata ) {
|
||||
this.videoWidth = data.loadeddata.videoWidth
|
||||
this.videoHeight = data.loadeddata.videoHeight
|
||||
this.updateHeight()
|
||||
}
|
||||
//用户点击了插槽内容,触发了点击函数
|
||||
if ( data.slotclick ) {
|
||||
this.slotHandles[data.slotclick] && this.slotHandles[data.slotclick]()//触发存储的函数
|
||||
}
|
||||
//用户点击了工具栏,触发了点击函数
|
||||
if ( data.toolclick ) {
|
||||
this.toolHandles[data.toolclick] && this.toolHandles[data.toolclick]()//触发存储的函数
|
||||
}
|
||||
// #ifdef APP-PLUS
|
||||
//用户在非5+环境点击了保存截图
|
||||
if ( data.capturesaved ) {
|
||||
if ( data.capturesaved.code != 200 ) {
|
||||
this.saveBase64ImageToAlbum(data.capturesaved.data.base64, data.capturesaved.fileName).then(path => {
|
||||
this.showToast('保存到相册成功')
|
||||
}).catch(() => {
|
||||
this.showToast('保存到相册失败')
|
||||
})
|
||||
}
|
||||
}
|
||||
//设置亮度
|
||||
if ( data.seeklight >= 0 ) {
|
||||
if ( this.customHandles.setLight ) {
|
||||
this.customHandles.setLight(data.seeklight)
|
||||
} else {
|
||||
uni.setScreenBrightness({
|
||||
value: data.seeklight
|
||||
})
|
||||
}
|
||||
}
|
||||
//设置音量
|
||||
if ( data.seekvolume >= 0 ) {
|
||||
if ( this.customHandles.setVolume ) {
|
||||
this.customHandles.setVolume(data.seekvolume)
|
||||
} else {
|
||||
plus.device.setVolume(data.seekvolume)
|
||||
}
|
||||
}
|
||||
//方向改变监听
|
||||
if ( data.directionchange ) {
|
||||
if ( data.directionchange.code != 200 ) {//锁定屏幕方向失败
|
||||
var direction = data.directionchange.to//预期屏幕方向
|
||||
if ( direction ) plus.screen.lockOrientation(direction)//锁定屏幕方向
|
||||
else plus.screen.unlockOrientation()//解除屏幕方向锁定
|
||||
}
|
||||
}
|
||||
//APP全屏改变监听
|
||||
if ( data.fullscreenchange ) {
|
||||
if ( data.fullscreenchange.type == 'css' ) {//使用css模拟全屏
|
||||
this.fullscreen = data.fullscreenchange.fullscreen
|
||||
if ( this.fullscreen ) {
|
||||
plus.navigator.setFullscreen(true)//调用系统全屏
|
||||
plus.navigator.hideSystemNavigation()//隐藏系统导航栏
|
||||
} else {
|
||||
plus.navigator.setFullscreen(false)//退出系统全屏
|
||||
setTimeout(function () {//不延迟,无法重新显示导航栏
|
||||
plus.navigator.showSystemNavigation()//隐藏系统导航栏
|
||||
}, 200)
|
||||
}
|
||||
if ( this.fullscreen ) {
|
||||
this._bindbackbutton = this.exitFullscreen.bind(this)
|
||||
plus.key.addEventListener("backbutton",this._bindbackbutton);//增加返回键监听,用于点击返回退出全屏
|
||||
} else {
|
||||
this._removeBackbuttonListener()//移除返回键监听事件
|
||||
}
|
||||
}
|
||||
if( !data.fullscreenchange.fullscreen ) this.updateHeight()//考虑到全屏时可能会加载新视频,退出全屏需要重新更新高度
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
//H5全屏改变监听
|
||||
if ( data.fullscreenchange ) {
|
||||
if ( data.fullscreenchange.type == 'css' ) {//使用css模拟全屏
|
||||
this.fullscreen = data.fullscreenchange.fullscreen
|
||||
}
|
||||
if ( !data.fullscreenchange.fullscreen ) this.updateHeight() //考虑到全屏时可能会加载新视频,退出全屏需要重新更新高度
|
||||
}
|
||||
// #endif
|
||||
//派发事件
|
||||
Object.keys(data).forEach(key => {
|
||||
if ( key != 'destroyed' ) this.$emit(key, data[key])
|
||||
})
|
||||
},
|
||||
//移除返回键监听
|
||||
_removeBackbuttonListener () {
|
||||
if ( this._bindbackbutton ) {
|
||||
plus.key.removeEventListener("backbutton",this._bindbackbutton);
|
||||
this._bindbackbutton = null
|
||||
}
|
||||
},
|
||||
//处理custom中的函数
|
||||
_traverseObject(obj, handleName) {
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return;
|
||||
}
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
const value = obj[key];
|
||||
if (typeof value === 'function') {
|
||||
const id = new Date().getTime().toString() + Math.round(Math.random() * 10000)
|
||||
this[handleName][id] = value
|
||||
obj[key] = id
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
this._traverseObject(value, handleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
//深度克隆数据,避免数据污染
|
||||
_deepClone (obj) {
|
||||
if(typeof obj !== "object" && typeof obj !== 'function') {
|
||||
//原始类型直接返回
|
||||
return obj;
|
||||
}
|
||||
var o = Object.prototype.toString.call(obj) === '[object Array]' ? [] : {};
|
||||
for(let i in obj) {
|
||||
if(obj.hasOwnProperty(i)){
|
||||
o[i] = i === 'loader' ? '' : typeof obj[i] === "object" ? this._deepClone(obj[i]) : obj[i];
|
||||
}
|
||||
}
|
||||
return o;
|
||||
},
|
||||
//更新高度
|
||||
updateHeight () {
|
||||
if ( this.updateTimer ) {
|
||||
clearTimeout(this.updateTimer)
|
||||
this.updateTimer = null
|
||||
}
|
||||
this.updateTimer = setTimeout(() => {
|
||||
//计算自适应高度
|
||||
if ( this.height == 'auto' && this.videoWidth ) {
|
||||
const windowWidth = uni.getSystemInfoSync().windowWidth
|
||||
const rate = windowWidth / this.videoWidth
|
||||
this.boxHeight = (this.videoHeight * rate) + 'px'
|
||||
}
|
||||
//继承父级高度
|
||||
if ( this.height == 'inherit' ) {
|
||||
this.boxHeight = '100%'
|
||||
}
|
||||
//像素高度
|
||||
if ( this.height.includes('px') ) {
|
||||
this.boxHeight = this.height
|
||||
}
|
||||
//计算百分比高度
|
||||
if ( this.height.includes('%') ) {
|
||||
const windowHeight = uni.getSystemInfoSync().windowHeight
|
||||
this.boxHeight = (windowHeight * (this.height.replace('%', '') / 100)) + 'px'
|
||||
}
|
||||
//去更新视频内部画布的尺寸
|
||||
this.$nextTick(() => {
|
||||
this.evalJS('updateSize')
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
//处理自定义配置
|
||||
async parseCustom () {
|
||||
const custom = this._deepClone(this.custom)
|
||||
//APP端设置初始化亮度和音量值
|
||||
// #ifdef APP-PLUS
|
||||
const initialLight = await this.getLight()
|
||||
const initialVolume = await this.getVolume()
|
||||
const gesture = custom.gesture || {}
|
||||
gesture.initialLight = gesture.initialLight || initialLight
|
||||
gesture.initialVolume = gesture.initialVolume || initialVolume
|
||||
custom.gesture = gesture
|
||||
// #endif
|
||||
//H5端,不能通过手势操作亮度和音量
|
||||
// #ifdef H5
|
||||
const gesture = custom.gesture || {}
|
||||
gesture.disableLight = true
|
||||
gesture.disableVolume = true
|
||||
custom.gesture = gesture
|
||||
// #endif
|
||||
//尝试获取自定义设置音量和亮度的方法
|
||||
this.customHandles.setLight = gesture.setLight
|
||||
this.customHandles.setVolume = gesture.setVolume
|
||||
this.customHandles.getLight = gesture.getLight
|
||||
this.customHandles.getVolume = gesture.getVolume
|
||||
gesture.setLight = null
|
||||
gesture.setVolume = null
|
||||
gesture.getLight = null
|
||||
gesture.getVolume = null
|
||||
//重置插槽函数
|
||||
this.slotHandles = {}
|
||||
this._traverseObject(custom, 'slotHandles')
|
||||
return custom
|
||||
},
|
||||
//处理文件链接(针对本地链接)
|
||||
parseSrc (src) {
|
||||
//本地static文件夹下的文件需要单独处理路径,否则无法加载
|
||||
if ( src.startsWith('/static') ) {
|
||||
// #ifdef H5
|
||||
const isHash = window.location.hash
|
||||
const pages = getCurrentPages()
|
||||
const page = pages[pages.length-1]
|
||||
const pathName = isHash ? window.location.pathname : window.location.pathname.replace(page.route, '')
|
||||
return window.location.origin + pathName + src.substring(1)
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
return plus.io.convertLocalFileSystemURL(src)
|
||||
// #endif
|
||||
}
|
||||
return src
|
||||
},
|
||||
//处理工具栏配置
|
||||
parseTool (data) {
|
||||
this.toolHandles = {}
|
||||
this._traverseObject(data, 'toolHandles')
|
||||
return data
|
||||
},
|
||||
//处理字幕|画质的链接
|
||||
parseList (data) {
|
||||
return data.map(item => {
|
||||
return {...item, src: this.parseSrc(item.src)}
|
||||
})
|
||||
},
|
||||
//处理切片列表链接
|
||||
parseSegments (data) {
|
||||
return data.map(item => {
|
||||
return {...item, url: this.parseSrc(item.url)}
|
||||
})
|
||||
},
|
||||
//处理通信参数
|
||||
parseArg (arg) {
|
||||
//必须转义2次,否则通信时可能会丢失字符
|
||||
return encodeURIComponent(encodeURIComponent(JSON.stringify(arg)))
|
||||
},
|
||||
//重加载视频
|
||||
async reloadVideo () {
|
||||
const arg = {
|
||||
src: this.parseSrc(this.src),
|
||||
segments: this.parseSegments(this.segments),
|
||||
title: this.title,
|
||||
poster: this.poster,
|
||||
type: this.type,
|
||||
three: this.three,
|
||||
initialTime: this.initialTime,
|
||||
duration: this.duration,
|
||||
autoplay: this.autoplay,
|
||||
preload: this.preload,
|
||||
muted: this.muted,
|
||||
loop: this.loop,
|
||||
playbackRate: this.playbackRate,
|
||||
isLive: this.isLive,
|
||||
header: this.header,
|
||||
controls: this.controls,
|
||||
objectFit: this.objectFit,
|
||||
crossOrigin: this.crossOrigin,
|
||||
openDirection: this.openDirection,
|
||||
exitDirection: this.exitDirection,
|
||||
quality: this.parseList(this.quality),
|
||||
works: this.works,
|
||||
workIndex: this.workIndex,
|
||||
subtitles: this.parseList(this.subtitles),
|
||||
subtitleIndex: this.subtitleIndex,
|
||||
custom: await this.parseCustom(),
|
||||
flvConfig: this.flvConfig,
|
||||
hlsConfig: this.hlsConfig,
|
||||
jsmpegConfig: this.jsmpegConfig
|
||||
}
|
||||
this.evalJS('reloadVideo', arg)
|
||||
},
|
||||
//卸载视频
|
||||
unload () {
|
||||
this.evalJS('destroy')
|
||||
},
|
||||
//加载弹幕
|
||||
loadDanmu () {
|
||||
// #ifdef APP-VUE || H5
|
||||
if ( this.renderType == 'renderjs' ) {
|
||||
this.evalJS('loadDanmu', this.danmu)
|
||||
} else {
|
||||
this.$refs.web && this.$refs.web.setData('danmu', this.danmu)
|
||||
setTimeout(() => {
|
||||
this.evalJS('loadDanmu')
|
||||
}, 100)
|
||||
}
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
const size = 100//分成100组传入,不然可能会超过字符串最大传输限制
|
||||
const list = this.danmu || []
|
||||
const len = Math.ceil(list.length / size)
|
||||
for ( let i = 0 ; i < len; i++ ) {
|
||||
const arr = list.slice(i * size, (i + 1) * size)
|
||||
const arg = {code: i < len - 1 ? 0 : 1, data: arr}
|
||||
this.evalJS('setDanmuData', arg)
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
//卸载弹幕
|
||||
unloadDanmu () {
|
||||
this.evalJS('unloadDanmu')
|
||||
},
|
||||
//发送弹幕
|
||||
sendDanmu (danmu, border) {
|
||||
this.evalJS('sendDanmu', danmu, border)
|
||||
},
|
||||
//插入弹幕
|
||||
insertDanmu (danmu) {
|
||||
this.evalJS('insertDanmu', danmu)
|
||||
},
|
||||
//动态修改video属性
|
||||
setVideo (key, value) {
|
||||
this.evalJS('setVideo', key, value)
|
||||
},
|
||||
//重加载自定义配置
|
||||
reloadCustom () {
|
||||
if ( this.customTimer ) {
|
||||
clearTimeout(this.customTimer)
|
||||
this.this.customTimer = null
|
||||
}
|
||||
this.customTimer = setTimeout(async () => {
|
||||
const custom = await this.parseCustom()
|
||||
this.evalJS('reloadCustom', custom)
|
||||
}, 200)
|
||||
},
|
||||
//更新配置
|
||||
updateConfig () {
|
||||
if ( this.updateTimer ) {
|
||||
clearTimeout(this.updateTimer)
|
||||
this.updateTimer = null
|
||||
}
|
||||
this.updateTimer = setTimeout(() => {
|
||||
const arg = {
|
||||
header: this.header,
|
||||
controls: this.controls
|
||||
}
|
||||
this.evalJS('updateConfig', arg)
|
||||
}, 200)
|
||||
},
|
||||
//播放视频
|
||||
play () {
|
||||
this.evalJS('play')
|
||||
},
|
||||
//暂停视频
|
||||
pause () {
|
||||
this.evalJS('pause')
|
||||
},
|
||||
//播放和暂停视频
|
||||
toggle () {
|
||||
this.evalJS('toggle')
|
||||
},
|
||||
/**
|
||||
* 跳转视频
|
||||
* @param {Number} time 跳转位置(单位秒)
|
||||
*/
|
||||
seek (time) {
|
||||
this.evalJS('seek', time)
|
||||
},
|
||||
/**
|
||||
* 开启视频全屏
|
||||
* @param {String} direction 屏幕方向 auto-自动计算 landscape-横屏 portrait-竖屏
|
||||
*/
|
||||
openFullscreen (direction) {
|
||||
this.evalJS('openFullscreen', direction)
|
||||
},
|
||||
//退出全屏
|
||||
exitFullscreen () {
|
||||
this.evalJS('exitFullscreen')
|
||||
},
|
||||
/**
|
||||
* 截图
|
||||
* @param {String} type video-使用video标签截图 3D-3D模式下截图渲染的canvas
|
||||
* @param {String} show 是否展示截图弹窗
|
||||
*/
|
||||
capture (type, show = true) {
|
||||
this.evalJS('capture', {type, show})
|
||||
},
|
||||
//消息提示
|
||||
showToast (data) {
|
||||
this.evalJS('showToast', data)
|
||||
},
|
||||
//展示工具栏
|
||||
showToolbar (data) {
|
||||
if ( typeof data == 'object' ) data = this.parseTool(data)
|
||||
this.evalJS('showToolbar', data)
|
||||
},
|
||||
//禁用手势事件
|
||||
disableGesture () {
|
||||
this.evalJS('disableGesture')
|
||||
},
|
||||
//启用手势事件
|
||||
enableGesture () {
|
||||
this.evalJS('enableGesture')
|
||||
},
|
||||
//获取屏幕亮度
|
||||
async getLight () {
|
||||
//是否传入了自定义获取亮度方法
|
||||
if ( this.customHandles.getLight ) {
|
||||
return await this.customHandles.getLight()
|
||||
} else {
|
||||
return new Promise(resolve => {
|
||||
uni.getScreenBrightness({
|
||||
success: res => {
|
||||
resolve(res.value)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
//获取设备音量
|
||||
getVolume () {
|
||||
//是否传入了自定义获取音量方法
|
||||
if ( this.customHandles.getVolume ) {
|
||||
return this.customHandles.getVolume()
|
||||
} else {
|
||||
return plus.device.getVolume()
|
||||
}
|
||||
},
|
||||
//监听组件销毁
|
||||
handleDestroy () {
|
||||
this.$emit('destroyed')
|
||||
},
|
||||
//保存base64图片到相册
|
||||
saveBase64ImageToAlbum (base64, fileName) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const basePath = '_doc'
|
||||
const dirPath = 'uniapp_temp'
|
||||
const tempFilePath = basePath + '/' + dirPath + '/' + fileName
|
||||
const bitmap = new plus.nativeObj.Bitmap(fileName)
|
||||
bitmap.loadBase64Data(base64, function() {
|
||||
bitmap.save(tempFilePath, {}, function() {
|
||||
bitmap.clear()
|
||||
plus.gallery.save(tempFilePath, function (e) {
|
||||
resolve(e.path)
|
||||
}, function (error) {
|
||||
reject(error)
|
||||
});
|
||||
}, function(error) {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
}, function(error) {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
//注入js,根据平台不同调用方式不同
|
||||
evalJS (name, ...args) {
|
||||
//生成注入js函数名
|
||||
let functionName = name + '('
|
||||
//拼接参数
|
||||
args.forEach(arg => {
|
||||
if ( typeof arg == 'object' ) {
|
||||
//是对象需要转义并加上双引号
|
||||
functionName += '"' + this.parseArg(arg) + '",'
|
||||
} else if ( typeof arg == 'string' ) {
|
||||
//字符串加上双引号
|
||||
functionName += '"' + arg + '",'
|
||||
} else {
|
||||
//其它类型直接拼接
|
||||
functionName += arg + ','
|
||||
}
|
||||
})
|
||||
//去掉最后一个逗号
|
||||
const lastIndex = functionName.lastIndexOf(',')
|
||||
if ( lastIndex > -1 ) functionName = functionName.substr(0, lastIndex)
|
||||
//收尾括号
|
||||
functionName += ');'
|
||||
// #ifdef APP-VUE || H5
|
||||
//如果时renderjs渲染方式,直接调用内部方法
|
||||
if ( this.renderType == 'renderjs' ) this.$refs.web && this.$refs.web[name](...args)
|
||||
//iframe渲染,注入js函数
|
||||
else this.$refs.web && this.$refs.web.evalJS(functionName)
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
//webview渲染,注入js函数
|
||||
this.$refs.web && this.$refs.web.evalJS(functionName)
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
//监听播放链接
|
||||
src (newVal) {
|
||||
if ( newVal && this.ready ) {
|
||||
this.reloadVideo()
|
||||
}
|
||||
},
|
||||
//监听切片列表
|
||||
segments: {
|
||||
handler(newVal, oldVal) {
|
||||
//src播放链接权重比切片列表高,传入播放链接的情况下不监听segments
|
||||
if ( !this.src ) this.reloadVideo()
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
//监听静音属性
|
||||
muted (newVal) {
|
||||
this.setVideo('muted', newVal)
|
||||
},
|
||||
//监听倍速属性
|
||||
playbackRate (newVal) {
|
||||
this.setVideo('playbackRate', newVal)
|
||||
},
|
||||
//监听循环属性
|
||||
loop (newVal) {
|
||||
this.setVideo('loop', newVal)
|
||||
},
|
||||
//监听高度
|
||||
height (newVal) {
|
||||
this.updateHeight()
|
||||
},
|
||||
//监听header
|
||||
header () {
|
||||
this.updateConfig()
|
||||
},
|
||||
//监听controls
|
||||
controls () {
|
||||
this.updateConfig()
|
||||
},
|
||||
//深度监听custom
|
||||
custom: {
|
||||
handler(newVal, oldVal) {
|
||||
this.reloadCustom()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* #ifndef APP-NVUE */
|
||||
.yingbing-video {
|
||||
width: 100%;
|
||||
}
|
||||
.yb-iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
/* #endif */
|
||||
.yingbing-video-full {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100vw!important;
|
||||
height: 100vh!important;
|
||||
z-index: 9999999;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
Reference in New Issue
Block a user