123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- <template>
- <div class="message-video">
- <div
- class="message-video-box"
- :class="[
- !data.progress &&
- data.message.status === 'success' &&
- isH5 &&
- 'message-video-cover'
- ]"
- @click="toggleShow"
- ref="skeleton"
- >
- <img
- class="message-img"
- v-if="(data.progress && poster) || (isH5 && poster)"
- :class="[isWidth ? 'isWidth' : 'isHeight']"
- :src="poster"
- />
- <video
- class="message-img video-h5-uploading"
- v-else-if="isH5"
- :src="data.url + '#t=0.1'"
- :poster="data.url"
- preload="auto"
- muted
- ref="video"
- ></video>
- <video
- class="message-img video-web"
- v-else-if="!data.progress && !isH5"
- :src="data.url"
- controls
- preload="metadata"
- :poster="poster"
- ref="video"
- ></video>
- <div class="progress" v-if="data.progress">
- <progress :value="data.progress" max="1"></progress>
- </div>
- </div>
- <div class="dialog-video" v-if="show && isH5" @click.self="toggleShow">
- <header>
- <i class="icon icon-close" @click.stop="toggleShow"></i>
- </header>
- <div
- class="dialog-video-box"
- :class="[isH5 ? 'dialog-video-h5' : '']"
- @click.self="toggleShow"
- >
- <video
- :class="[isWidth ? 'isWidth' : 'isHeight']"
- :src="data.url"
- controls
- autoplay
- ></video>
- </div>
- </div>
- </div>
- </template>
- <script lang="ts">
- import {
- defineComponent,
- watchEffect,
- reactive,
- toRefs,
- computed,
- nextTick,
- ref,
- watch
- } from 'vue';
- import { handleSkeletonSize } from '../utils/utils';
- export default defineComponent({
- props: {
- data: {
- type: Object,
- default: () => ({})
- },
- isH5: {
- type: Boolean,
- default: false
- }
- },
- setup(props: any, ctx: any) {
- const data = reactive({
- data: {},
- show: false,
- poster: '',
- posterWidth: 0,
- posterHeight: 0
- });
- const skeleton = ref();
- const video = ref();
- const isWidth = computed(() => {
- // eslint-disable-next-line no-unsafe-optional-chaining
- const { snapshotWidth = 0, snapshotHeight = 0 } = (data.data as any)
- ?.message?.payload;
- return snapshotWidth >= snapshotHeight;
- });
- const transparentPosterUrl =
- 'https://web.sdk.qcloud.com/im/assets/images/transparent.png';
- const toggleShow = () => {
- if (!(data.data as any).progress) {
- data.show = !data.show;
- }
- };
- // h5 部分浏览器(safari / wx)video标签 封面为空 在视频未上传完成前的封面展示需要单独进行处理截取
- const getVideoBase64 = (url: string) => {
- return new Promise(function (resolve, reject) {
- let dataURL = '';
- let video = document.createElement('video');
- video.setAttribute('crossOrigin', 'anonymous'); //处理跨域
- video.setAttribute('src', url);
- video.setAttribute('preload', 'auto');
- video.addEventListener(
- 'loadeddata',
- function () {
- let canvas = document.createElement('canvas'),
- width = video.videoWidth, //canvas的尺寸和图片一样
- height = video.videoHeight;
- canvas.width = width;
- canvas.height = height;
- (canvas as any)
- .getContext('2d')
- .drawImage(video, 0, 0, width, height); //绘制canvas
- dataURL = canvas.toDataURL('image/jpeg'); //转换为base64
- data.posterWidth = width;
- data.posterHeight = height;
- resolve(dataURL);
- },
- { once: true }
- );
- });
- };
- const handlePosterUrl = async (data: any) => {
- if (!data) return '';
- if (data.progress) {
- return await getVideoBase64(data.url);
- } else {
- return (
- (data.snapshotUrl !== transparentPosterUrl && data.snapshotUrl) ||
- (data?.message?.payload?.snapshotUrl !== transparentPosterUrl &&
- data?.message?.payload?.snapshotUrl) ||
- (data?.message?.payload?.thumbUrl !== transparentPosterUrl &&
- data?.message?.payload?.thumbUrl) ||
- (await getVideoBase64(data.url))
- );
- }
- };
- watchEffect(async () => {
- data.data = props.data;
- if (!data.data) return;
- data.poster = await handlePosterUrl(data.data);
- nextTick(async () => {
- const containerWidth =
- document.getElementById('messageEle')?.clientWidth || 0;
- const max = props.isH5 ? Math.min(containerWidth - 172, 300) : 300;
- let size;
- if (!(data.data as any).progress) {
- let {
- snapshotWidth = 0,
- snapshotHeight = 0,
- snapshotUrl
- } = data.data as any;
- if (snapshotWidth === 0 || snapshotHeight === 0) return;
- if (snapshotUrl === transparentPosterUrl) {
- snapshotWidth = data.posterWidth;
- snapshotHeight = data.posterHeight;
- }
- size = handleSkeletonSize(snapshotWidth, snapshotHeight, max, max);
- skeleton?.value?.style &&
- (skeleton.value.style.width = `${size.width}Px`);
- skeleton?.value?.style &&
- (skeleton.value.style.height = `${size.height}Px`);
- if (!props.isH5) {
- video?.value?.style &&
- (video.value.style.width = `${size.width}Px`);
- video?.value?.style &&
- (video.value.style.height = `${size.height}Px`);
- }
- } else {
- ctx.emit('uploading');
- }
- });
- });
- watch(
- () => (data.data as any)?.progress,
- (newVal, oldVal) => {
- if (!newVal && oldVal) {
- ctx.emit('uploading');
- }
- }
- );
- return {
- ...toRefs(data),
- toggleShow,
- isWidth,
- getVideoBase64,
- handlePosterUrl,
- skeleton,
- video
- };
- }
- });
- </script>
- <style lang="scss" scoped>
- @import url('../../../styles/common.scss');
- @import url('../../../styles/icon.scss');
- .message-video {
- position: relative;
- display: flex;
- justify-content: center;
- overflow: hidden;
- &-box {
- max-width: min(calc(100vw - 180Px), 300Px);
- video {
- max-width: min(calc(100vw - 180Px), 300Px);
- max-height: min(calc(100vw - 180Px), 300Px);
- width: inherit;
- height: inherit;
- border-radius: 10Px;
- }
- img {
- max-width: min(calc(100vw - 180Px), 300Px);
- max-height: min(calc(100vw - 180Px), 300Px);
- width: inherit;
- height: inherit;
- border-radius: 10Px;
- }
- img[src=''],
- img:not([src]) {
- opacity: 0;
- }
- }
- &-cover {
- display: inline-block;
- position: relative;
- &::before {
- position: absolute;
- z-index: 1;
- content: '';
- width: 0Px;
- height: 0Px;
- border: 10Px solid transparent;
- border-left: 15Px solid #ffffff;
- top: 0;
- left: 0;
- bottom: 0;
- right: 0;
- margin: auto;
- transform: translate(5Px, 0Px);
- }
- video {
- max-width: min(calc(100vw - 180Px), 300Px);
- max-height: min(calc(100vw - 180Px), 300Px);
- width: inherit;
- height: inherit;
- border-radius: 10Px;
- }
- }
- .progress {
- position: absolute;
- box-sizing: border-box;
- width: 100%;
- height: 100%;
- padding: 0 20Px;
- border-radius: 10Px;
- left: 0;
- top: 0;
- background: rgba(#000000, 0.5);
- display: flex;
- align-items: center;
- flex: 1;
- progress {
- color: #006eff;
- appearance: none;
- border-radius: 0.25rem;
- background: rgba(#ffffff, 1);
- width: 100%;
- height: 0.5rem;
- &::-webkit-progress-value {
- background-color: #006eff;
- border-radius: 0.25rem;
- }
- &::-webkit-progress-bar {
- border-radius: 0.25rem;
- background: rgba(#ffffff, 1);
- }
- &::-moz-progress-bar {
- color: #006eff;
- background: #006eff;
- border-radius: 0.25rem;
- }
- }
- }
- }
- .dialog-video {
- position: fixed;
- z-index: 12;
- width: 100vw;
- height: 100vh;
- top: 0;
- left: 0;
- display: flex;
- flex-direction: column;
- align-items: center;
- header {
- display: flex;
- justify-content: flex-end;
- background: rgba(0, 0, 0, 0.49);
- width: 100%;
- box-sizing: border-box;
- padding: 10Px 10Px;
- }
- &-box {
- display: flex;
- flex: 1;
- max-height: 100%;
- padding: 6rem;
- box-sizing: border-box;
- justify-content: center;
- align-items: center;
- video {
- max-width: 100%;
- max-height: 100%;
- }
- }
- }
- .dialog-video-h5 {
- width: 100%;
- height: 100%;
- background: #000000;
- padding: 30Px 0;
- }
- .isWidth {
- width: 100%;
- }
- .isHeight {
- height: 100%;
- }
- </style>
|