vue 预览图片封装上下切换预览功能封装处理
点击图片放大,再点击关闭实现很简单,但是当多个图预览时候,能自带切换体验会更好一些。
本文使用ant-design-vue作为项目案例
父组件调用:
<!-- 预览 -->
<a-modal :visible="previewImgVisible" :footer="null" centered width="auto" @cancel="handleCancel">
<Review :path="previewImgUrl" :pathList="previewImgUrlList" />
</a-modal>
data() {
return {
previewImgVisible: false, // 查看附件图片
previewImgUrl: '',
previewImgUrlList: [], //预览图片列表
}
},
methods: {
// 关闭预览
handleCancel() {
this.previewImgVisible = false;
},
// 预览图片
handlePreview(url) {
this.previewImgUrl = url;
this.previewImgVisible = true;
this.previewImgUrlList = ['url1', 'url2', 'url3']
},
}
预览子组件
Review.vue
<template>
<div
class="cp-view"
:style="{
width: (width < 500 ? 500 : width) + 50 + 'px',
height: (height < 300 ? 300 : height) + 50 + 'px',
}"
>
<div class="photo-box flex-row-center-center" @mousewheel="onWheel">
<img
:class="['photo-item', isCovered && 'drag']"
ref="photoRef"
:src="currentPath"
alt=""
@mousedown="onDrag"
:style="{ transform: `rotate(${rotateDeg}deg) scale(${scale})` }"
/>
</div>
<div class="photo-btns flex-row-center-center">
<a-icon
class="pointer fs-22 m-r-30"
type="zoom-in"
@click="onScale(0.1)"
/>
<a-icon
class="pointer fs-22 m-r-30"
type="zoom-out"
@click="onScale(-0.1)"
/>
<span
:class="['pointer', 'reset-btn', 'm-r-30', scale === 1 && 'disabled']"
@click="scale = 1"
>1:1</span
>
<a-icon class="pointer fs-22" type="redo" @click="onRotate" />
</div>
<a-icon
:class="['icon-left', pathList.indexOf(currentPath) === 0 && 'disabled']"
@click="handleSwiper(-1)"
type="left-circle"
/>
<a-icon
:class="[
'icon-right',
pathList.indexOf(currentPath) === pathList.length - 1 && 'disabled',
]"
@click="handleSwiper(1)"
type="right-circle"
/>
</div>
</template>
<script>
export default {
props: {
path: {
type: String,
default: '',
},
pathList: {
type: Array,
default: () => [],
},
},
data() {
return {
currentPath: this.path,
rotateDeg: 0,
width: 0,
height: 0,
cWidth: 0,
cHeight: 0,
scale: 1,
isCovered: false,
imgRef: this.$refs.photoRef,
};
},
mounted() {
},
methods: {
// 图片前后切换
handleSwiper(prevOrNext) {
const index = this.pathList.indexOf(this.currentPath)
if ((index === this.pathList.length - 1 && prevOrNext === 1) || (index === 0 && prevOrNext === -1)) return
console.log(index)
if (prevOrNext === -1) {
this.currentPath = this.pathList[index - 1]
} else if (prevOrNext === 1) {
this.currentPath = this.pathList[index + 1]
}
},
onDrag(e) {
const img = this.$refs.photoRef;
e.preventDefault();
const l = e.clientX - img.offsetLeft;
const t = e.clientY - img.offsetTop;
document.onmousemove = function (e) {
img.style.left = e.clientX - l + 'px';
img.style.top = e.clientY - t + 'px';
};
img.onmouseup = function () {
document.onmousemove = null;
img.onmouseup = null;
};
},
onRotate() {
this.rotateDeg += 90;
if (this.rotateDeg >= 360) {
this.rotateDeg = 0;
}
const img = this.$refs.photoRef;
if (this.rotateDeg === 90 || this.rotateDeg === 270) {
if (this.cWidth > this.cHeight) {
// 宽大于高
img.width = this.height;
} else if (this.cWidth < this.cHeight) {
// 宽小于高
img.height = this.height;
}
} else {
// 还原
if (this.cWidth > this.cHeight) {
// 宽大于高
img.width = this.width;
} else if (this.cWidth < this.cHeight) {
// 宽小于高
img.height = this.height;
}
}
},
onScale(val) {
if (val < 0 && this.scale <= 0.2) {
return;
}
if (val > 0 && this.scale >= 4) {
return;
}
this.scale += val;
},
onWheel(e) {
if (e.deltaY > 0) {
this.onScale(-0.1);
} else if (e.deltaY < 0) {
this.onScale(0.1);
}
},
},
watch: {
currentPath: {
immediate: true,
deep: true,
handler: function (val) {
this.rotateDeg = 0;
const img = document.createElement('img');
img.src = val || '';
img.onload = () => {
let w = (this.cWidth = img.width);
let h = (this.cHeight = img.height);
if (w > 1000) {
w = 1000;
}
if (h > 500) {
h = 500;
}
if (w < 500) {
h = 500;
}
if (h < 300) {
h = 300;
}
this.width = w;
this.height = h;
};
},
},
scale: {
handler: function (val) {
const img = this.$refs.photoRef;
if (
img.width * val > this.width + 50 ||
img.height * val > this.height + 50
) {
this.isCovered = true;
} else {
this.isCovered = false;
}
},
},
},
};
</script>
<style lang="less" scoped>
.cp-view {
position: relative;
margin-bottom: 40px;
&:hover {
.icon-left,
.icon-right {
display: block;
}
}
.icon-left,
.icon-right {
user-select: none;
position: absolute;
font-size: 34px;
cursor: pointer;
top: 50%;
transform: translateY(-50);
display: none;
&.disabled {
color: #999;
cursor: not-allowed;
}
}
.icon-left {
left: 0;
}
.icon-right {
right: 0;
}
// overflow: hidden;
.photo-box {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
.photo-item {
position: absolute;
max-height: 500px;
max-width: 1000px;
display: block;
&.drag {
cursor: move;
}
}
}
.photo-btns {
width: 100%;
user-select: none;
position: absolute;
bottom: -40px;
.reset-btn {
padding: 1px 5px;
margin-top: -3px;
background: #00be7e;
border: 1px solid #00be7e;
border-radius: 2px;
color: #fff;
&.disabled {
border-color: #ccc;
background: none;
color: #333;
cursor: default;
}
}
}
}
</style>
效果如下: