first commit
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
<template>
|
||||
<view
|
||||
v-if="dataVisible"
|
||||
:id="classPrefix"
|
||||
:class="classPrefix + ' ' + tClass"
|
||||
:style="tools._style([customStyle, '--td-image-viewer-top: ' + distanceTop + 'px'])"
|
||||
:aria-modal="true"
|
||||
aria-role="dialog"
|
||||
aria-label="图片查看器"
|
||||
@touchmove.stop.prevent="true"
|
||||
>
|
||||
<view
|
||||
:class="classPrefix + '__mask'"
|
||||
data-source="overlay"
|
||||
:style="tools._style([backgroundColor && '--td-image-viewer-mask-bg-color: ' + backgroundColor])"
|
||||
aria-role="button"
|
||||
aria-label="关闭"
|
||||
@click="(e) => onClose(e, 'overlay')"
|
||||
/>
|
||||
<block v-if="images && images.length">
|
||||
<view :class="classPrefix + '__content'">
|
||||
<swiper
|
||||
class="swiper"
|
||||
:style="swiperStyle[currentSwiperIndex] && swiperStyle[currentSwiperIndex].style"
|
||||
:autoplay="false"
|
||||
:current="currentSwiperIndex"
|
||||
tabindex="0"
|
||||
@change="onSwiperChange"
|
||||
@click="(e) => onClose(e, '')"
|
||||
>
|
||||
<swiper-item
|
||||
v-for="(item, index) in images"
|
||||
:key="index"
|
||||
:class="classPrefix + '__preview-image'"
|
||||
>
|
||||
<t-image
|
||||
v-if="!lazy || shouldLoadImage(index, currentSwiperIndex, loadedImageIndexes)"
|
||||
:t-class="prefix + '-image--external'"
|
||||
:class="classPrefix + '__image'"
|
||||
:custom-style="(imagesStyle[index] && imagesStyle[index].style) || ''"
|
||||
:data-index="index"
|
||||
:src="item"
|
||||
:mode="(imageProps && imageProps.mode) || 'aspectFit'"
|
||||
:lazy="(imageProps && imageProps.lazy) || false"
|
||||
:loading="(imageProps && imageProps.loading) || 'default'"
|
||||
:shape="(imageProps && imageProps.shape) || 'square'"
|
||||
:webp="(imageProps && imageProps.webp) || false"
|
||||
:show-menu-by-longpress="(imageProps && imageProps.showMenuByLongpress) || false"
|
||||
@load="onImageLoadSuccess($event, { index })"
|
||||
/>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
<view
|
||||
:class="classPrefix + '__nav'"
|
||||
>
|
||||
<view
|
||||
:class="classPrefix + '__nav-close'"
|
||||
aria-role="button"
|
||||
aria-label="关闭"
|
||||
@click.stop.prevent="(e) => onClose(e, '')"
|
||||
>
|
||||
<slot name="close-btn" />
|
||||
<block
|
||||
v-if="_closeBtn"
|
||||
name="icon"
|
||||
>
|
||||
<t-icon
|
||||
:custom-style="_closeBtn.style || ''"
|
||||
:t-class="_closeBtn.tClass"
|
||||
:prefix="_closeBtn.prefix"
|
||||
:name="_closeBtn.name"
|
||||
:size="_closeBtn.size"
|
||||
:color="_closeBtn.color"
|
||||
:aria-hidden="!!_closeBtn.ariaHidden"
|
||||
:aria-label="_closeBtn.ariaLabel"
|
||||
:aria-role="_closeBtn.ariaRole"
|
||||
@click="_closeBtn.click || ''"
|
||||
/>
|
||||
</block>
|
||||
</view>
|
||||
<view
|
||||
v-if="showIndex"
|
||||
:class="classPrefix + '__nav-index'"
|
||||
>
|
||||
{{ currentSwiperIndex + 1 }}/{{ images.length }}
|
||||
</view>
|
||||
<view
|
||||
:class="classPrefix + '__nav-delete'"
|
||||
aria-role="button"
|
||||
aria-label="删除"
|
||||
@click="onDelete"
|
||||
>
|
||||
<slot name="delete-btn" />
|
||||
<t-icon
|
||||
v-if="_deleteBtn"
|
||||
:custom-style="_deleteBtn.style || ''"
|
||||
:t-class="_deleteBtn.tClass"
|
||||
:prefix="_deleteBtn.prefix"
|
||||
:name="_deleteBtn.name"
|
||||
:size="_deleteBtn.size"
|
||||
:color="_deleteBtn.color"
|
||||
:aria-hidden="!!_deleteBtn.ariaHidden"
|
||||
:aria-label="_deleteBtn.ariaLabel"
|
||||
:aria-role="_deleteBtn.ariaRole"
|
||||
@click="_deleteBtn.click || ''"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import TImage from '../image/image';
|
||||
import TIcon from '../icon/icon';
|
||||
import { uniComponent } from '../common/src/index';
|
||||
import { styles, calcIcon, systemInfo } from '../common/utils';
|
||||
import { prefix } from '../common/config';
|
||||
import props from './props';
|
||||
import tools from '../common/utils.wxs';
|
||||
import { shouldLoadImage } from './computed.js';
|
||||
import useCustomNavbar from '../mixins/using-custom-navbar';
|
||||
|
||||
|
||||
const name = `${prefix}-image-viewer`;
|
||||
|
||||
export default uniComponent({
|
||||
name,
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
controlledProps: [
|
||||
{
|
||||
key: 'visible',
|
||||
event: 'close',
|
||||
},
|
||||
],
|
||||
externalClasses: [`${prefix}-class`],
|
||||
mixins: [useCustomNavbar],
|
||||
components: {
|
||||
TImage,
|
||||
TIcon,
|
||||
},
|
||||
props: {
|
||||
...props,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
prefix,
|
||||
classPrefix: name,
|
||||
currentSwiperIndex: 0,
|
||||
loadedImageIndexes: [],
|
||||
windowHeight: 0,
|
||||
windowWidth: 0,
|
||||
swiperStyle: {},
|
||||
imagesStyle: {},
|
||||
maskTop: 0,
|
||||
tools,
|
||||
|
||||
_deleteBtn: null,
|
||||
_closeBtn: null,
|
||||
dataVisible: this.visible,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible: {
|
||||
handler(v) {
|
||||
this.dataVisible = v;
|
||||
this.init();
|
||||
},
|
||||
},
|
||||
initialIndex: 'init',
|
||||
images: 'init',
|
||||
|
||||
closeBtn: {
|
||||
handler(v) {
|
||||
this._closeBtn = calcIcon(v, 'close');
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
|
||||
deleteBtn: {
|
||||
handler(v) {
|
||||
this._deleteBtn = calcIcon(v, 'delete');
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.saveScreenSize();
|
||||
// this.calcMaskTop();
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
shouldLoadImage,
|
||||
init() {
|
||||
const { visible: dataVisible, images, initialIndex } = this;
|
||||
if (dataVisible && images?.length) {
|
||||
this.loadedImageIndexes = [];
|
||||
this.currentSwiperIndex = initialIndex >= images.length ? images.length - 1 : initialIndex;
|
||||
}
|
||||
},
|
||||
calcMaskTop() {
|
||||
if (this.usingCustomNavbar) {
|
||||
const rect = wx?.getMenuButtonBoundingClientRect() || null;
|
||||
const { statusBarHeight } = systemInfo;
|
||||
|
||||
if (rect && statusBarHeight) {
|
||||
this.maskTop = rect.top - statusBarHeight + rect.bottom;
|
||||
}
|
||||
}
|
||||
},
|
||||
saveScreenSize() {
|
||||
const { windowHeight, windowWidth } = systemInfo;
|
||||
this.windowHeight = windowHeight;
|
||||
this.windowWidth = windowWidth;
|
||||
},
|
||||
|
||||
calcImageDisplayStyle(imageWidth, imageHeight) {
|
||||
const { windowWidth, windowHeight } = this;
|
||||
const ratio = imageWidth / imageHeight;
|
||||
// 图片宽高都小于屏幕宽高
|
||||
if (imageWidth < windowWidth && imageHeight < windowHeight) {
|
||||
return {
|
||||
styleObj: {
|
||||
width: `${imageWidth}px`,
|
||||
height: `${imageHeight}px`,
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
},
|
||||
};
|
||||
}
|
||||
// 图片宽高至少存在一个大于屏幕宽高,此时判断图片宽高比,按长边显示
|
||||
if (ratio >= 1) {
|
||||
return {
|
||||
styleObj: {
|
||||
width: '100vw',
|
||||
height: `${(windowWidth / ratio)}px`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// 图片的高大于宽(纵向图),设定高度为100vh,宽度自适应,且确保宽度不超过屏幕宽度
|
||||
const scaledHeight = ratio * windowHeight * 2;
|
||||
if (scaledHeight < windowWidth) {
|
||||
return {
|
||||
styleObj: {
|
||||
width: `${scaledHeight}rpx`,
|
||||
height: '100vh',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
},
|
||||
};
|
||||
}
|
||||
// 当通过高度计算的图片宽度超过屏幕宽度时, 以屏幕宽度为基准, 重新计算高度
|
||||
return {
|
||||
styleObj: {
|
||||
width: '100vw',
|
||||
height: `${(windowWidth / imageWidth) * imageHeight}px`,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
onImageLoadSuccess({ e }, { index }) {
|
||||
const {
|
||||
detail: { width, height },
|
||||
} = e;
|
||||
|
||||
const { mode, styleObj } = this.calcImageDisplayStyle(width, height);
|
||||
const originImagesStyle = this.imagesStyle;
|
||||
const originSwiperStyle = this.swiperStyle;
|
||||
|
||||
if (!this.loadedImageIndexes.includes(index)) {
|
||||
this.loadedImageIndexes = [...this.loadedImageIndexes, index];
|
||||
}
|
||||
|
||||
|
||||
this.swiperStyle = {
|
||||
...originSwiperStyle,
|
||||
[index]: {
|
||||
style: `height: ${styleObj.height}`,
|
||||
},
|
||||
};
|
||||
this.imagesStyle = {
|
||||
...originImagesStyle,
|
||||
[index]: {
|
||||
mode,
|
||||
style: styles({ ...styleObj }),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
onSwiperChange(e) {
|
||||
const {
|
||||
detail: { current },
|
||||
} = e;
|
||||
this.currentSwiperIndex = current;
|
||||
|
||||
this._trigger('change', { index: current });
|
||||
},
|
||||
|
||||
onClose(e, source) {
|
||||
this._trigger('close', { visible: false, trigger: source || 'button', index: this.currentSwiperIndex });
|
||||
},
|
||||
|
||||
onDelete() {
|
||||
this._trigger('delete', { index: this.currentSwiperIndex });
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
@import './image-viewer.css';
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user