Image加载完成前的默认渲染处理

This commit is contained in:
bob
2025-04-17 09:42:06 +08:00
parent 96f15c1022
commit 8ed65c1358
6 changed files with 104 additions and 54 deletions

View File

@@ -84,10 +84,10 @@ const onSave = async () => {
isLoading.value = true
try {
const md5 = await getMd5(file)
const thumbObj = await prehandleImage(file)
const prehandleImageObj = await prehandleImage(file)
const files = {
originFile: file,
thumbFile: thumbObj.thumbFile
thumbFile: prehandleImageObj.thumbFile
}
const requestBody = {
storeType: 0,
@@ -95,10 +95,10 @@ const onSave = async () => {
fileName: file.name,
fileRawType: file.type,
size: file.size,
originWidth: thumbObj.originWidth,
originHeight: thumbObj.originHeight,
thumbWidth: thumbObj.thumbWidth,
thumbHeight: thumbObj.thumbHeight
originWidth: prehandleImageObj.originWidth,
originHeight: prehandleImageObj.originHeight,
thumbWidth: prehandleImageObj.thumbWidth,
thumbHeight: prehandleImageObj.thumbHeight
}
const res = await mtsUploadServiceForImage(requestBody, files)
emit('update:newAvatar', {

View File

@@ -4,24 +4,40 @@ import { ElImage } from 'element-plus'
import { formatFileSize } from '@/js/utils/common'
import { useImageStore } from '@/stores'
const props = defineProps(['sessionId', 'imgId', 'isForMix'])
const props = defineProps(['sessionId', 'imgId', 'isForMix', 'thumbWidth', 'thumbHeight'])
const emits = defineEmits(['load'])
const imageData = useImageStore()
const onLoad = (e) => {
const img = e.target
const maxWidth = props.isForMix ? Math.min(img.naturalWidth, 360) : 360
const maxHeight = props.isForMix ? Math.min(img.naturalHeight, 180) : 180
const maxWidth = computed(() => {
return props.isForMix ? Math.min(props.thumbWidth, 360) : 360
})
if (img.naturalWidth / img.naturalHeight > maxWidth / maxHeight) {
img.style.width = maxWidth + 'px'
img.style.height = 'auto'
const maxHeight = computed(() => {
return props.isForMix ? Math.min(props.thumbHeight, 270) : 270
})
const renderWidth = computed(() => {
if (!props.thumbWidth || !props.thumbHeight) {
return 360 // 如果拿不到缩略图大小,默认以 360*270 尺寸显示
} else if (props.thumbWidth / props.thumbHeight > maxWidth.value / maxHeight.value) {
return maxWidth.value
} else {
img.style.height = maxHeight + 'px'
img.style.width = 'auto'
return (props.thumbWidth / props.thumbHeight) * maxHeight.value
}
})
const renderHeight = computed(() => {
if (!props.thumbWidth || !props.thumbHeight) {
return 270 // 如果拿不到缩略图大小,默认以 360*270 尺寸显示
} else if (props.thumbWidth / props.thumbHeight > maxWidth.value / maxHeight.value) {
return (props.thumbHeight / props.thumbWidth) * maxWidth.value
} else {
return maxHeight.value
}
})
const onLoad = async () => {
emits('load') //向父组件暴露load事件
}
@@ -70,8 +86,15 @@ const formatSize = computed(() => {
:infinite="false"
:lazy="false"
fit="contain"
:style="{ width: `${renderWidth}px`, height: `${renderHeight}px` }"
@load="onLoad"
>
<template #placeholder>
<div
class="image-msg-placeholder loading"
:style="{ width: `${renderWidth}px`, height: `${renderHeight}px` }"
></div>
</template>
</el-image>
<div v-if="fileName || size > 0" class="info">
<span class="name item text-ellipsis" :title="fileName">
@@ -125,4 +148,30 @@ const formatSize = computed(() => {
}
}
}
.image-msg-placeholder {
background-color: #000;
}
.image-msg-placeholder.loading::before {
content: '';
box-sizing: border-box;
position: absolute;
top: 50%;
left: 50%;
width: 30px;
height: 30px;
margin-top: -15px;
margin-left: -15px;
border-radius: 50%;
border: 3px solid #ccc;
border-top-color: #007bff;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>

View File

@@ -118,23 +118,24 @@ const parseContent = async (callbacks) => {
// base64编码的图片
const file = base64ToFile(insert.image, uuidv4()) // base64转file
const tempObjectId = new Date().getTime()
contentFromLocal[index] = `{${tempObjectId}}`
// 发送的时候设置本地缓存(非服务端数据),用于立即渲染
const md5 = await getMd5(file)
const prehandleImageObj = await prehandleImage(file)
const localSrc = URL.createObjectURL(file)
imageData.setImage({
objectId: tempObjectId,
originUrl: localSrc,
thumbUrl: localSrc,
thumbUrl: localSrc, // 本地缓存缩略图用的是原图
fileName: file.name,
size: file.size,
thumbWidth: prehandleImageObj.originWidth,
thumbHeight: prehandleImageObj.originHeight,
createdTime: new Date()
})
contentFromLocal[index] = `{${tempObjectId}}`
const md5 = await getMd5(file)
const thumbObj = await prehandleImage(file)
const files = {
originFile: file,
thumbFile: thumbObj.thumbFile
thumbFile: prehandleImageObj.thumbFile
}
const requestBody = {
storeType: 0,
@@ -142,10 +143,10 @@ const parseContent = async (callbacks) => {
fileName: file.name,
fileRawType: file.type,
size: file.size,
originWidth: thumbObj.originWidth,
originHeight: thumbObj.originHeight,
thumbWidth: thumbObj.thumbWidth,
thumbHeight: thumbObj.thumbHeight
originWidth: prehandleImageObj.originWidth,
originHeight: prehandleImageObj.originHeight,
thumbWidth: prehandleImageObj.thumbWidth,
thumbHeight: prehandleImageObj.thumbHeight
}
//上传图片至服务端

View File

@@ -1,8 +1,7 @@
<script setup>
import { ref } from 'vue'
import { Clock, Microphone } from '@element-plus/icons-vue'
import { ElLoading, ElMessage } from 'element-plus'
import { el_loading_options } from '@/const/commonConst'
import { ElMessage } from 'element-plus'
import EmojiIcon from '@/assets/svg/emoji.svg'
import FileIcon from '@/assets/svg/file.svg'
import ImageIcon from '@/assets/svg/image.svg'
@@ -41,25 +40,23 @@ const onSelectedFile = async (file) => {
let contentType = msgContentType.DOCUMENT
let md5
let thumbObj
let videoObj
let prehandleImageObj
let prehandleVideoObj
let msg = {}
let loadingInstance
try {
loadingInstance = ElLoading.service(el_loading_options)
md5 = await getMd5(file.raw)
if (file.raw.type.startsWith('image/')) {
contentType = msgContentType.IMAGE
thumbObj = await prehandleImage(file.raw)
prehandleImageObj = await prehandleImage(file.raw)
} else if (file.raw.type.startsWith('audio/')) {
contentType = msgContentType.AUDIO
} else if (file.raw.type.startsWith('video/')) {
contentType = msgContentType.VIDEO
videoObj = await prehandleVideo(file.raw)
prehandleVideoObj = await prehandleVideo(file.raw)
}
setLocalData(contentType, file, videoObj)
setLocalData(contentType, file, prehandleImageObj, prehandleVideoObj)
emit('saveLocalMsg', {
contentType: contentType,
@@ -70,8 +67,6 @@ const onSelectedFile = async (file) => {
})
} catch (error) {
return
} finally {
loadingInstance.close()
}
let requestApi = mtsUploadService
@@ -85,15 +80,15 @@ const onSelectedFile = async (file) => {
const files = { originFile: file.raw }
if (contentType === msgContentType.IMAGE) {
requestBody.originWidth = thumbObj.originWidth
requestBody.originHeight = thumbObj.originHeight
requestBody.thumbWidth = thumbObj.thumbWidth
requestBody.thumbHeight = thumbObj.thumbHeight
files.thumbFile = thumbObj.thumbFile
requestBody.originWidth = prehandleImageObj.originWidth
requestBody.originHeight = prehandleImageObj.originHeight
requestBody.thumbWidth = prehandleImageObj.thumbWidth
requestBody.thumbHeight = prehandleImageObj.thumbHeight
files.thumbFile = prehandleImageObj.thumbFile
requestApi = mtsUploadServiceForImage
} else if (contentType === msgContentType.VIDEO) {
requestBody.videoWidth = videoObj.width
requestBody.videoHeight = videoObj.height
requestBody.videoWidth = prehandleVideoObj.width
requestBody.videoHeight = prehandleVideoObj.height
}
messageData.updateMsg(msg.sessionId, msg.msgId, {
@@ -130,16 +125,18 @@ const onSelectedFile = async (file) => {
* @param contentType
* @param file
*/
const setLocalData = (contentType, file, videoObj) => {
const setLocalData = (contentType, file, prehandleImageObj, prehandleVideoObj) => {
const localSrc = URL.createObjectURL(file.raw)
switch (contentType) {
case msgContentType.IMAGE:
imageData.setImage({
objectId: file.uid,
originUrl: localSrc,
thumbUrl: localSrc,
thumbUrl: localSrc, // 本地缓存缩略图用的是原图
fileName: file.name,
size: file.raw.size,
thumbWidth: prehandleImageObj.originWidth,
thumbHeight: prehandleImageObj.originHeight,
createdTime: new Date()
})
break
@@ -157,8 +154,8 @@ const setLocalData = (contentType, file, videoObj) => {
downloadUrl: localSrc,
fileName: file.name,
size: file.raw.size,
width: videoObj.width,
height: videoObj.height
width: prehandleVideoObj.width,
height: prehandleVideoObj.height
})
break
case msgContentType.DOCUMENT:

View File

@@ -177,6 +177,8 @@ const renderImage = (content, isForMix = false) => {
sessionId: props.sessionId,
imgId,
isForMix,
thumbWidth: imageData.image[imgId].thumbWidth,
thumbHeight: imageData.image[imgId].thumbHeight,
onLoad: () => {
emit('loadFinished')
}

View File

@@ -17,19 +17,19 @@ const formatSize = computed(() => {
const renderWidth = computed(() => {
if (!props.width || !props.height) {
return 480
return 480 // 如果拿不到视频大小,默认以 480*270 尺寸播放
} else if (props.width > props.height) {
return 480
} else {
return Math.floor((props.width / props.height) * 320)
return (props.width / props.height) * 320
}
})
const renderHeight = computed(() => {
if (!props.width || !props.height) {
return 270
return 270 // 如果拿不到视频大小,默认以 480*270 尺寸播放
} else if (props.width > props.height) {
return Math.floor((props.height / props.width) * 480)
return (props.height / props.width) * 480
} else {
return 320
}
@@ -55,7 +55,7 @@ onMounted(() => {
player.on('ready', () => {
// 监听视频元数据加载完成事件
const videoElement = player.root.querySelector('video')
videoElement.addEventListener('loadedmetadata', async () => {
videoElement.addEventListener('loadedmetadata', () => {
videoWrapperRef.value.style.width = `${renderWidth.value}px`
videoWrapperRef.value.style.height = `${renderHeight.value}px`
videoWrapperRef.value.style.padding = 0
@@ -68,7 +68,8 @@ onMounted(() => {
<template>
<div
class="video-msg-wrapper loading"
class="video-msg-wrapper"
:class="{ loading: !isLoaded }"
:style="{ width: `${renderWidth}px`, height: `${renderHeight}px` }"
>
<div v-show="isLoaded" ref="videoWrapperRef" :id="`msg-xgplayer-${props.videoId}`"></div>