perf: 优化移动端图片预览

This commit is contained in:
kuaifan 2022-05-31 15:15:54 +08:00
parent 022499300e
commit 1f8198b36b
10 changed files with 190 additions and 34 deletions

View File

@ -758,14 +758,19 @@ class FileController extends AbstractController
$file->saveBeforePids();
//
$data = Base::uploadMove($data, "uploads/file/" . $file->type . "/" . date("Ym") . "/" . $file->id . "/");
$content = [
'from' => '',
'type' => $type,
'ext' => $data['ext'],
'url' => $data['path'],
];
if (isset($data['width'])) {
$content['width'] = $data['width'];
$content['height'] = $data['height'];
}
$content = FileContent::createInstance([
'fid' => $file->id,
'content' => [
'from' => '',
'type' => $type,
'ext' => $data['ext'],
'url' => $data['path']
],
'content' => $content,
'text' => '',
'size' => $file->size,
'userid' => $user->userid,

View File

@ -341,6 +341,8 @@ class File extends AbstractModel
$content = Base::json2array(FileContent::whereFid($item['id'])->orderByDesc('id')->value('content'));
if ($content) {
$item['image_url'] = Base::fillUrl($content['url']);
$item['image_width'] = intval($content['width']);
$item['image_height'] = intval($content['height']);
}
}
return $item;

View File

@ -35,6 +35,7 @@
"moment": "^2.29.1",
"node-sass": "^6.0.1",
"notification-koro1": "^1.1.1",
"photoswipe": "^5.2.7",
"postcss": "^8.4.5",
"quill": "^1.3.7",
"quill-mention-hi": "^3.1.0-1",

View File

@ -0,0 +1,82 @@
<template>
</template>
<style lang="scss">
body {
.preview-image-swipe {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
> img {
max-width: 100%;
max-height: 100%;
}
}
}
</style>
<script>
import PhotoSwipeLightbox from 'photoswipe/lightbox';
import 'photoswipe/style.css';
export default {
props: {
urlList: {
type: Array,
default: () => []
},
initialIndex: {
type: Number,
default: 0
},
},
data() {
return {
lightbox: null,
};
},
beforeDestroy() {
this.lightbox?.destroy();
},
watch: {
urlList: {
handler(array) {
this.lightbox?.destroy();
const dataSource = array.map(item => {
if ($A.isJson(item)) {
if (item.src) {
item.src = $A.rightDelete(item.src, "_thumb.jpg");
}
return item
}
return {
html: `<div class="preview-image-swipe"><img src="${$A.rightDelete(item, "_thumb.jpg")}"/></div>`,
}
})
this.lightbox = new PhotoSwipeLightbox({
dataSource,
escKey: false,
showHideAnimationType: 'none',
pswpModule: () => import('photoswipe'),
});
this.lightbox.on('close', () => {
this.$emit("on-close")
});
this.lightbox.on('destroy', () => {
this.$emit("on-destroy")
});
this.lightbox.init();
this.lightbox.loadAndOpen(this.initialIndex);
},
immediate: true
},
initialIndex(index) {
this.lightbox?.loadAndOpen(index);
}
},
};
</script>

View File

@ -72,6 +72,10 @@
z-index: 2;
transform: translateY(-50%);
@media (max-width: 640px) {
display: none;
}
&.is-disabled {
cursor: no-drop;
@ -208,7 +212,11 @@ export default {
return this.index === this.urlList.length - 1;
},
currentImg() {
return $A.rightDelete(this.urlList[this.index], "_thumb.jpg");
let item = this.urlList[this.index];
if ($A.isJson(item)) {
item = item.src;
}
return $A.rightDelete(item, "_thumb.jpg");
},
imgStyle() {
const {scale, deg, offsetX, offsetY, enableTransition} = this.transform;

View File

@ -5,16 +5,19 @@
:mask-closable="false"
:footer-hide="true"
:transition-names="['', '']"
fullscreen
class-name="common-preview-image">
<PreviewImageView v-if="list.length > 0" :initial-index="index" :url-list="list" infinite/>
:class-name="mode === 'desktop' ? 'common-preview-image-view' : 'common-preview-image-swipe'"
fullscreen>
<template v-if="list.length > 0">
<PreviewImageView v-if="mode === 'desktop'" :initial-index="index" :url-list="list" infinite/>
<PreviewImageSwipe v-if="mode === 'mobile'" :initial-index="index" :url-list="list" @on-destroy="show=false"/>
</template>
</Modal>
</template>
<style lang="scss">
body {
.ivu-modal-wrap {
&.common-preview-image {
&.common-preview-image-view {
.ivu-modal {
margin: 0;
padding: 0;
@ -34,6 +37,11 @@ body {
top: 40px;
width: 40px;
@media (max-width: 640px) {
right: 24px;
top: 24px;
}
.ivu-icon-ios-close {
top: 0;
right: 0;
@ -48,16 +56,20 @@ body {
}
}
}
&.common-preview-image-swipe {
display: none;
}
}
}
</style>
<script>
import PreviewImageView from "./view";
const PreviewImageView = () => import('./components/view');
const PreviewImageSwipe = () => import('./components/swipe');
export default {
name: 'PreviewImage',
components: {PreviewImageView},
components: {PreviewImageSwipe, PreviewImageView},
props: {
value: {
type: Boolean,
@ -72,6 +84,12 @@ export default {
default: () => {
return [];
}
},
mode: {
type: String,
default: () => {
return $A.isDesktop ? 'desktop' : 'mobile';
}
}
},
data() {

View File

@ -375,7 +375,7 @@ export default {
//
} else {
this.timerScroll = setInterval(() => {
if (this.quill.hasFocus()) {
if (this.quill?.hasFocus()) {
$A.scrollToView(this.$refs.editor, true)
} else {
clearInterval(this.timerScroll);

View File

@ -152,7 +152,7 @@ export default {
contentClass() {
const {type, msg} = this.msgData;
let classArray = [];
const classArray = [];
if (type === 'text') {
if (/^<img\s+class="emoticon"[^>]*?>$/.test(msg.text)) {
classArray.push('an-emoticon')
@ -243,15 +243,19 @@ export default {
//
const array = text.match(/<img\s+[^>]*?>/g);
if (array) {
const widthReg = new RegExp("width=\"(\\d+)\"")
const heightReg = new RegExp("height=\"(\\d+)\"")
const widthReg = new RegExp("width=\"(\\d+)\""),
heightReg = new RegExp("height=\"(\\d+)\"")
array.some(res => {
if (widthReg.test(res) && heightReg.test(res)) {
let width = parseInt(res.match(widthReg)[1]),
height = parseInt(res.match(heightReg)[1]),
const widthMatch = res.match(widthReg),
heightMatch = res.match(heightReg);
if (widthMatch && heightMatch) {
const width = parseInt(widthMatch[1]),
height = parseInt(heightMatch[1]),
maxSize = res.indexOf("emoticon") > -1 ? 150 : 220;
let scale = $A.scaleToScale(width, height, maxSize, maxSize);
let value = res.replace(widthReg, `width=${scale.width}`).replace(heightReg, `height=${scale.height}`)
const scale = $A.scaleToScale(width, height, maxSize, maxSize);
const value = res
.replace(widthReg, `original-width="${width}" width="${scale.width}"`)
.replace(heightReg, `original-height="${height}" height="${scale.height}"`)
text = text.replace(res, value)
}
})
@ -261,14 +265,14 @@ export default {
recordStyle(info) {
const {duration} = info;
let width = 50 + Math.min(180, Math.floor(duration / 150));
const width = 50 + Math.min(180, Math.floor(duration / 150));
return {
width: width + 'px',
};
},
recordDuration(duration) {
let minute = Math.floor(duration / 60000),
const minute = Math.floor(duration / 60000),
seconds = Math.floor(duration / 1000) % 60;
if (minute > 0) {
return `${minute}:${seconds}`
@ -340,7 +344,7 @@ export default {
this.viewPicture(target.currentSrc);
} else {
this.$store.state.previewImageIndex = 0;
this.$store.state.previewImageList = [target.currentSrc];
this.$store.state.previewImageList = this.getTextImageInfos(target.outerHTML);
}
break;
@ -400,20 +404,20 @@ export default {
return a.id - b.id;
});
//
let list = [];
const list = [];
data.some(({type, msg}) => {
if (type === 'file') {
list.push(msg.path)
} else if (type === 'text') {
const baseUrl = $A.apiUrl('../');
const array = msg.text.match(/<img\s+class="browse"[^>]*?src="(.*?)"[^>]*?>/g);
array && array.some(res => {
list.push(res.match(/<img\s+class="browse"[^>]*?src="(.*?)"[^>]*?>/)[1].replace(/\{\{RemoteURL\}\}/g, baseUrl))
list.push({
src: msg.path,
width: msg.width,
height: msg.height,
})
} else if (type === 'text') {
list.push(...this.getTextImageInfos(msg.text))
}
})
//
let index = list.findIndex(item => item === currentUrl);
const index = list.findIndex(({src}) => src === currentUrl);
if (index > -1) {
this.$store.state.previewImageIndex = index;
this.$store.state.previewImageList = list;
@ -423,6 +427,30 @@ export default {
}
},
getTextImageInfos(text) {
const baseUrl = $A.apiUrl('../');
const array = text.match(new RegExp(`<img[^>]*?>`, "g"));
const list = [];
if (array) {
const srcReg = new RegExp("src=\"(.*?)\""),
widthReg = new RegExp("(original-)?width=\"(\\d+)\""),
heightReg = new RegExp("(original-)?height=\"(\\d+)\"")
array.some(res => {
const srcMatch = res.match(srcReg),
widthMatch = res.match(widthReg),
heightMatch = res.match(heightReg);
if (srcMatch && widthMatch && heightMatch) {
list.push({
src: srcMatch[1].replace(/\{\{RemoteURL\}\}/g, baseUrl),
width: widthMatch[2],
height: heightMatch[2],
})
}
})
}
return list;
},
downFile() {
$A.modalConfirm({
title: '下载文件',

View File

@ -951,7 +951,16 @@ export default {
const list = this.fileList.filter(({image_url}) => !!image_url)
if (list.length > 0) {
this.imageIndex = list.findIndex(({id}) => item.id === id)
this.imageList = list.map(item => item.image_url)
this.imageList = list.map(item => {
if (item.image_width) {
return {
src: item.image_url,
width: item.image_width,
height: item.image_height,
}
}
return item.image_url;
})
this.imageShow = true
return;
}

View File

@ -676,5 +676,8 @@
}
}
}
.file-upload-list {
bottom: 86px
}
}
}