|
@@ -7,7 +7,7 @@
|
|
|
style="width: 100%"
|
|
style="width: 100%"
|
|
|
>
|
|
>
|
|
|
<!-- @click.stop="closeOnImg" @touchstart.capture="onTouchStart" -->
|
|
<!-- @click.stop="closeOnImg" @touchstart.capture="onTouchStart" -->
|
|
|
- <view class="nut-imagepreview" @click.stop="closeOnImg" @touchstart.capture="onTouchStart">
|
|
|
|
|
|
|
+ <view class="nut-imagepreview" ref="swipeRef">
|
|
|
<nut-swiper
|
|
<nut-swiper
|
|
|
v-if="showPop"
|
|
v-if="showPop"
|
|
|
:auto-play="autoplay"
|
|
:auto-play="autoplay"
|
|
@@ -20,12 +20,31 @@
|
|
|
:pagination-visible="paginationVisible"
|
|
:pagination-visible="paginationVisible"
|
|
|
:pagination-color="paginationColor"
|
|
:pagination-color="paginationColor"
|
|
|
>
|
|
>
|
|
|
- <nut-swiper-item v-for="(item, index) in videos" :key="index">
|
|
|
|
|
- <nut-video :source="item.source" :options="item.options"></nut-video>
|
|
|
|
|
- </nut-swiper-item>
|
|
|
|
|
- <nut-swiper-item v-for="(item, index) in images" :key="index">
|
|
|
|
|
- <img :src="item.src" class="nut-imagepreview-img" />
|
|
|
|
|
- </nut-swiper-item>
|
|
|
|
|
|
|
+ <template v-if="videos.length">
|
|
|
|
|
+ <image-preview-item
|
|
|
|
|
+ v-for="(item, index) in videos"
|
|
|
|
|
+ :key="index"
|
|
|
|
|
+ :video="item"
|
|
|
|
|
+ :rootHeight="rootHeight"
|
|
|
|
|
+ :rootWidth="rootWidth"
|
|
|
|
|
+ :show="showPop"
|
|
|
|
|
+ :init-no="active"
|
|
|
|
|
+ @close="onClose"
|
|
|
|
|
+ :maxZoom="maxZoom"
|
|
|
|
|
+ :minZoom="minZoom"
|
|
|
|
|
+ ></image-preview-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <template v-for="(item, index) in images" :key="index">
|
|
|
|
|
+ <image-preview-item
|
|
|
|
|
+ :image="item"
|
|
|
|
|
+ :rootHeight="rootHeight"
|
|
|
|
|
+ :rootWidth="rootWidth"
|
|
|
|
|
+ :show="showPop"
|
|
|
|
|
+ :init-no="active"
|
|
|
|
|
+ @close="onClose"
|
|
|
|
|
+ ></image-preview-item>
|
|
|
|
|
+ </template>
|
|
|
</nut-swiper>
|
|
</nut-swiper>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="nut-imagepreview-index" v-if="showIndex"> {{ active }} / {{ images.length + videos.length }} </view>
|
|
<view class="nut-imagepreview-index" v-if="showIndex"> {{ active }} / {{ images.length + videos.length }} </view>
|
|
@@ -43,6 +62,7 @@ import Swiper from '../swiper/index.vue';
|
|
|
import SwiperItem from '../swiperitem/index.vue';
|
|
import SwiperItem from '../swiperitem/index.vue';
|
|
|
import Icon from '../icon/index.vue';
|
|
import Icon from '../icon/index.vue';
|
|
|
import { isPromise } from '@/packages/utils/util.ts';
|
|
import { isPromise } from '@/packages/utils/util.ts';
|
|
|
|
|
+import ImagePreviewItem from './imagePreviewItem.vue';
|
|
|
const { componentName, create } = createComponent('imagepreview');
|
|
const { componentName, create } = createComponent('imagepreview');
|
|
|
|
|
|
|
|
export default create({
|
|
export default create({
|
|
@@ -77,7 +97,7 @@ export default create({
|
|
|
},
|
|
},
|
|
|
autoplay: {
|
|
autoplay: {
|
|
|
type: [Number, String],
|
|
type: [Number, String],
|
|
|
- default: 3000
|
|
|
|
|
|
|
+ default: 0
|
|
|
},
|
|
},
|
|
|
isWrapTeleport: {
|
|
isWrapTeleport: {
|
|
|
type: Boolean,
|
|
type: Boolean,
|
|
@@ -99,7 +119,15 @@ export default create({
|
|
|
type: String,
|
|
type: String,
|
|
|
default: 'top-right' // top-right top-left
|
|
default: 'top-right' // top-right top-left
|
|
|
},
|
|
},
|
|
|
- beforeClose: Function
|
|
|
|
|
|
|
+ beforeClose: Function,
|
|
|
|
|
+ minZoom: {
|
|
|
|
|
+ type: Number,
|
|
|
|
|
+ default: 1 / 3
|
|
|
|
|
+ },
|
|
|
|
|
+ maxZoom: {
|
|
|
|
|
+ type: Number,
|
|
|
|
|
+ default: 3
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
emits: ['close', 'change'],
|
|
emits: ['close', 'change'],
|
|
|
components: {
|
|
components: {
|
|
@@ -107,30 +135,31 @@ export default create({
|
|
|
[Video.name]: Video,
|
|
[Video.name]: Video,
|
|
|
[Swiper.name]: Swiper,
|
|
[Swiper.name]: Swiper,
|
|
|
[SwiperItem.name]: SwiperItem,
|
|
[SwiperItem.name]: SwiperItem,
|
|
|
- [Icon.name]: Icon
|
|
|
|
|
|
|
+ [Icon.name]: Icon,
|
|
|
|
|
+ ImagePreviewItem: ImagePreviewItem
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
setup(props, { emit }) {
|
|
setup(props, { emit }) {
|
|
|
const { show, images } = toRefs(props);
|
|
const { show, images } = toRefs(props);
|
|
|
|
|
|
|
|
|
|
+ const swipeRef = ref();
|
|
|
|
|
+
|
|
|
const state = reactive({
|
|
const state = reactive({
|
|
|
showPop: false,
|
|
showPop: false,
|
|
|
active: 1,
|
|
active: 1,
|
|
|
maxNo: 1,
|
|
maxNo: 1,
|
|
|
- source: {
|
|
|
|
|
- src: 'https://storage.jd.com/about/big-final.mp4?Expires=3730193075&AccessKey=3LoYX1dQWa6ZXzQl&Signature=ViMFjz%2BOkBxS%2FY1rjtUVqbopbJI%3D',
|
|
|
|
|
- type: 'video/mp4'
|
|
|
|
|
- },
|
|
|
|
|
- options: {
|
|
|
|
|
- muted: true,
|
|
|
|
|
- controls: true
|
|
|
|
|
- },
|
|
|
|
|
- eleImg: null,
|
|
|
|
|
- store: {
|
|
|
|
|
- scale: 1,
|
|
|
|
|
- moveable: false
|
|
|
|
|
- },
|
|
|
|
|
- lastTouchEndTime: 0 // 用来辅助监听双击
|
|
|
|
|
|
|
+ rootWidth: 0,
|
|
|
|
|
+ rootHeight: 0
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const styles = computed(() => {
|
|
|
|
|
+ let style: any = {};
|
|
|
|
|
+ if (props.closeIconPosition == 'top-right') {
|
|
|
|
|
+ style.right = '10px';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ style.left = '10px';
|
|
|
|
|
+ }
|
|
|
|
|
+ return style;
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const styles = computed(() => {
|
|
const styles = computed(() => {
|
|
@@ -148,13 +177,6 @@ export default create({
|
|
|
emit('change', state.active);
|
|
emit('change', state.active);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const closeOnImg = () => {
|
|
|
|
|
- // 点击内容区域的图片是否可以关闭弹层(视频区域由于nut-video做了限制,无法关闭弹层)
|
|
|
|
|
- if (props.contentClose) {
|
|
|
|
|
- onClose();
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
const onClose = () => {
|
|
const onClose = () => {
|
|
|
if (props.beforeClose) {
|
|
if (props.beforeClose) {
|
|
|
const returnVal = props.beforeClose.apply(null, state.active);
|
|
const returnVal = props.beforeClose.apply(null, state.active);
|
|
@@ -175,110 +197,13 @@ export default create({
|
|
|
// 执行关闭
|
|
// 执行关闭
|
|
|
const closeDone = () => {
|
|
const closeDone = () => {
|
|
|
state.showPop = false;
|
|
state.showPop = false;
|
|
|
- state.store.scale = 1;
|
|
|
|
|
- scaleNow();
|
|
|
|
|
state.active = 1;
|
|
state.active = 1;
|
|
|
emit('close');
|
|
emit('close');
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- // 计算两个点的距离
|
|
|
|
|
- const getDistance = (first: any, second: any) => {
|
|
|
|
|
- // 计算两个点起始时刻的距离和终止时刻的距离,终止时刻距离变大了则放大,变小了则缩小
|
|
|
|
|
- // 放大 k 倍则 scale 也 扩大 k 倍
|
|
|
|
|
- return Math.hypot(Math.abs(second.x - first.x), Math.abs(second.y - first.y));
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const scaleNow = () => {
|
|
|
|
|
- (state.eleImg as any).style.transform = 'scale(' + state.store.scale + ')';
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const onTouchStart = (event: any) => {
|
|
|
|
|
- // console.log('start');
|
|
|
|
|
- // 如果已经放大,双击应变回原尺寸;如果是原尺寸,双击应放大
|
|
|
|
|
- const curTouchTime = new Date().getTime();
|
|
|
|
|
- if (curTouchTime - state.lastTouchEndTime < 300) {
|
|
|
|
|
- const store = state.store;
|
|
|
|
|
- if (store.scale > 1) {
|
|
|
|
|
- store.scale = 1;
|
|
|
|
|
- } else if (store.scale == 1) {
|
|
|
|
|
- store.scale = 2;
|
|
|
|
|
- }
|
|
|
|
|
- scaleNow();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var touches = event.touches;
|
|
|
|
|
- var events = touches[0];
|
|
|
|
|
- var events2 = touches[1];
|
|
|
|
|
-
|
|
|
|
|
- // event.preventDefault();
|
|
|
|
|
-
|
|
|
|
|
- const store = state.store as any;
|
|
|
|
|
- store.moveable = true;
|
|
|
|
|
-
|
|
|
|
|
- if (events2) {
|
|
|
|
|
- // 如果开始两指操作,记录初始时刻两指间的距离
|
|
|
|
|
- store.oriDistance = getDistance(
|
|
|
|
|
- {
|
|
|
|
|
- x: events.pageX,
|
|
|
|
|
- y: events.pageY
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- x: events2.pageX,
|
|
|
|
|
- y: events2.pageY
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
- // 取到开始两指操作时的放大(缩小比例),store.scale 存储的是当前的放缩比(相对于标准大小 scale 为 1 的情况的放大缩小比)
|
|
|
|
|
- store.originScale = store.scale || 1;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const onTouchMove = (event: any) => {
|
|
|
|
|
- if (!state.store.moveable) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- const store = state.store as any;
|
|
|
|
|
- // event.preventDefault();
|
|
|
|
|
- var touches = event.touches;
|
|
|
|
|
- var events = touches[0];
|
|
|
|
|
- var events2 = touches[1];
|
|
|
|
|
- // 双指移动
|
|
|
|
|
- if (events2) {
|
|
|
|
|
- // 获得当前两点间的距离
|
|
|
|
|
- const curDistance = getDistance(
|
|
|
|
|
- {
|
|
|
|
|
- x: events.pageX,
|
|
|
|
|
- y: events.pageY
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- x: events2.pageX,
|
|
|
|
|
- y: events2.pageY
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- /** 此处计算倍数,距离放大(缩小) k 倍则 scale 也 扩大(缩小) k 倍。距离放大(缩小)倍数 = 结束时两点距离 除以 开始时两点距离
|
|
|
|
|
- * 注意此处的 scale 变化是基于 store.scale 的。
|
|
|
|
|
- * store.scale 是一个暂存值,比如第一次放大 2 倍,则 store.scale 为 2。
|
|
|
|
|
- * 再次两指触碰的时候,store.originScale 就为 store.scale 的值,基于此时的 store.scale 继续放大缩小。 **/
|
|
|
|
|
- const curScale = curDistance / store.oriDistance;
|
|
|
|
|
- store.scale = store.originScale * curScale;
|
|
|
|
|
-
|
|
|
|
|
- // 最大放大 3 倍,缩小后松手要弹回原比例
|
|
|
|
|
- if (store.scale > 3) {
|
|
|
|
|
- store.scale = 3;
|
|
|
|
|
- }
|
|
|
|
|
- scaleNow();
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const onTouchEnd = () => {
|
|
|
|
|
- // console.log('end');
|
|
|
|
|
- state.lastTouchEndTime = new Date().getTime();
|
|
|
|
|
- const store = state.store as any;
|
|
|
|
|
- store.moveable = false;
|
|
|
|
|
- if ((store.scale < 1.1 && store.scale > 1) || store.scale < 1) {
|
|
|
|
|
- store.scale = 1;
|
|
|
|
|
- scaleNow();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 点击关闭按钮
|
|
|
|
|
+ const handleCloseIcon = () => {
|
|
|
|
|
+ onClose();
|
|
|
};
|
|
};
|
|
|
// 点击关闭按钮
|
|
// 点击关闭按钮
|
|
|
const handleCloseIcon = () => {
|
|
const handleCloseIcon = () => {
|
|
@@ -286,10 +211,10 @@ export default create({
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const init = () => {
|
|
const init = () => {
|
|
|
- state.eleImg = document.querySelector('.nut-imagepreview') as any;
|
|
|
|
|
- document.addEventListener('touchmove', onTouchMove);
|
|
|
|
|
- document.addEventListener('touchend', onTouchEnd);
|
|
|
|
|
- document.addEventListener('touchcancel', onTouchEnd);
|
|
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ state.rootHeight = swipeRef.value.offsetHeight;
|
|
|
|
|
+ state.rootWidth = swipeRef.value.offsetWidth;
|
|
|
|
|
+ }, 100);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
watch(
|
|
watch(
|
|
@@ -308,15 +233,11 @@ export default create({
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
|
|
+ swipeRef,
|
|
|
...toRefs(state),
|
|
...toRefs(state),
|
|
|
slideChangeEnd,
|
|
slideChangeEnd,
|
|
|
onClose,
|
|
onClose,
|
|
|
- closeOnImg,
|
|
|
|
|
- onTouchStart,
|
|
|
|
|
- onTouchMove,
|
|
|
|
|
- onTouchEnd,
|
|
|
|
|
- getDistance,
|
|
|
|
|
- scaleNow,
|
|
|
|
|
|
|
+ handleCloseIcon,
|
|
|
styles
|
|
styles
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|