<template>
<div
class="ue-media-preview"
:class="[`ue-media-preview-${type}`]"
:style="getMaskBg"
v-show="visible"
>
<transition>
<div
class="ue-media-preview-warp"
data-ue-loading-spinner-type="snake"
data-ue-loading-color="rgb(94, 129, 244)"
data-ue-loading-mask-color="rgba(0, 0, 0, 0)"
data-ue-loading-spinner-color="rgb(94, 129, 244)"
data-ue-loading-text="加载中......"
v-ue-loading="loading"
>
<template v-if="type=== 'image'">
<img :src="url" @error="imgError" @load="imgLoad" :style="getStyle()" />
</template>
<template v-if="type=== 'video'">
<video
ref="video"
height="100%"
controls="false"
:autoplay="autoplay"
loop="loop"
:muted="muted"
preload="metadata"
:src="url"
:class="{'loading':loading}"
></video>
</template>
<i class="ue-media-preview-close el-icon-circle-close" @click="doDestory" v-if="!loading"></i>
</div>
</transition>
</div>
</template>
<script>
export default {
name: 'ueMediaPreview',
model: {
prop: 'val',
event: 'update'
},
props: {
val: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'image',
validator (value) {
return [
'image',
'video'
].indexOf(value) > -1
}
},
maskColor: {
type: String,
default: 'rgba(0, 0, 0, .5)'
},
url: {
type: String,
require: true
},
posterUrl: {
type: String
},
autoplay: {
type: Boolean,
value: false
}
},
data () {
return {
visible: this.val,
muted: false,
loading: true
}
},
computed: {
getMaskBg () {
const { maskColor } = this
return {
backgroundColor: maskColor
}
}
},
watch: {
val (newVal) {
this.visible = newVal
}
},
methods: {
getStyle () {
return {
'opacity': this.loading ? 0 : 1,
'max-height': document.documentElement.clientHeight * 0.8 + 'px'
}
},
imgLoad (e) {
this.loading = false
},
imgError (e) {
this.imgLoad(e)
this.doDestory()
this.$message.warning('预览失败')
},
videoDone (e) {
this.loading = false
},
videoError (e) {
this.videoDone(e)
this.doDestory()
this.$message.warning('预览失败')
},
doDestory () {
this.updateStatus(false)
window.URL.revokeObjectURL(this.url)
this.$destroy(true)
if (this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el)
}
},
updateStatus (status = true) {
this.visible = status
this.$emit('update', status)
},
addVideoEvent () {
this.$nextTick(() => {
const { type, videoDone, videoError } = this
if (type === 'video') {
const _video = this.$refs.video
_video.addEventListener('canplaythrough', videoDone)
_video.addEventListener('error', videoError)
this.$once('hook:beforeDestroy', () => {
if (_video) {
_video.removeEventListener('canplaythrough', videoDone)
_video.removeEventListener('error', videoError)
}
})
}
})
}
},
mounted () {
this.addVideoEvent()
}
}
</script>
<style lang="scss">
.ue-media-preview {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 10009;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.ue-media-preview-warp {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-width: 70px;
.ue-loading {
width: 100%;
.ue-loading-warp {
padding: 0;
}
}
}
.ue-media-preview-close {
position: absolute;
right: -40px;
top: -30px;
color: #fff;
font-size: 28px;
font-weight: normal;
cursor: pointer;
}
&.ue-media-preview-image {
.ue-media-preview-warp {
img {
-webkit-user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
border-radius: 8px;
overflow: hidden;
}
}
}
&.ue-media-preview-video {
.ue-media-preview-warp {
height: 70%;
video {
border-radius: 8px;
overflow: hidden;
object-fit: fill;
background: #000;
&.loading {
opacity: 0;
background: transparent;
}
}
}
}
}
</style>
console