插件不好用,还是自己手写一个简易版的vue组件图片浏览器吧
模版层,CV即用。底部图片列表要用缩略图哦,用原图会影响性能。
里面使用了一些阿里字体 iconfont 自行替换哦。
<template>
<div class="picture-viewer-box" v-if="images.length" @click="hide">
<div class="main-body">
<div class="content">
<img class="unselectable" :class="{imgAnimation: showImge}" @click.stop="stop" :style="`width: ${img.width}; height: ${img.height}; transform: translate(-50%, -50%) rotate(${img.rotate}deg);`" @load="imgLoad" draggable="false" v-drag :src="images[current].url" alt="" />
<img v-if="!showImge" class="add-loading" src="https://static.acc5.com/static_acc5/2018/images/l_4.gif" alt="">
<template v-if="images.length > 1">
<div class="switchover-img iconfont icon-right" @click.stop="changeImage('next')"></div>
<div class="switchover-img iconfont icon-left" @click.stop="changeImage('up')"></div>
</template>
</div>
<div class="viewer-box">
<div class="list" v-for="(item, index) in images" :key="index" @click.stop="changeImage('change', index)">
<img width="100%" :src="item.thumb" alt="">
<div class="viewer-mask" v-if="index !== current"></div>
</div>
</div>
</div>
</div>
</template>
script层,主要逻辑功能是使用到自定义指令(directives)实现的一个图片的拖拽,放大、缩小、旋转均为css:transform属性。
调用方式为引入组件通过对组件设置ref调用组件的show方法传入一个图片列表(必传)、一个要显示的下标(非必填默认第一个)。
也可以把组件注入到项目主入口如App.vue或者layout里。通过中央数据总线bus来进行监听调用。
<script>
export default {
data() {
return {
current: 0, // 当前显示的图片下标
showImge: false, // 图片加载成功animation展示动画
img: {
width: 'auto',
height: '100%',
rotate: 0,
}, // 图片的宽高
images: [], // 图片列表
zoomMultiples: 26, // 图片缩放倍数
};
},
methods: {
imgLoad(e) { // 图片加载成功回调
this.$nextTick(() => {
let imgEl = document.getElementsByClassName('unselectable')[0];
let width = imgEl.offsetWidth;
let height = imgEl.offsetHeight;
// 检测图片是否比屏幕还大
if(width > document.documentElement.clientWidth) {
width = document.documentElement.clientWidth
}
if(height > document.documentElement.clientHeight) {
height = document.documentElement.clientHeight
}
this.img.width = width + 'px';
this.img.height = height + 'px';
this.showImge = true;
})
},
// 旋转图片
rotateImage() {
this.img.rotate += 90;
},
show(list, current) { // 父组件调用传入一个图片的数组和显示第几个
this.current = current || 0;
this.images = list;
document.getElementsByTagName("body")[0].style.overflow = "hidden";
},
hide() { // 隐藏图片组件
document.getElementsByTagName("body")[0].style.overflow = "visible";
this.clearImg()
this.images = [];
},
changeImage(type, i) {
let listLength = this.images.length - 1;
if(type == 'next') {
if(this.current == listLength) return
this.clearImg()
this.current += 1;
} else if (type == 'up') {
if(this.current == 0) return
this.clearImg()
this.current -= 1;
} else if (type == 'change') {
if(this.current == i) return
this.clearImg()
this.current = i;
}
},
clearImg() {
this.img.width = 'auto';
this.img.height = '100%';
this.img.rotate = 0;
document.getElementsByClassName('unselectable')[0].style.left = '50%'
document.getElementsByClassName('unselectable')[0].style.top = '50%'
this.showImge = false;
},
stop() {},
// 向上滚动放大
upScroll() {
this.img.width = parseInt(this.img.width) + this.zoomMultiples + 'px';
this.img.height = parseInt(this.img.height) + this.zoomMultiples + 'px';
},
// 向下滚动缩小
downScroll() {
if(parseInt(this.img.width) < 100 || parseInt(this.img.height) < 100) return
this.img.width = parseInt(this.img.width) - this.zoomMultiples + 'px';
this.img.height = parseInt(this.img.height) - this.zoomMultiples + 'px';
},
// 监听鼠标滑轮滚动
wheelScroll(e) {
if(!this.images.length) return
e = e || window.event;
if (e.wheelDelta) {
// 判断浏览器IE,谷歌滑轮事件
if (e.wheelDelta > 0) {
// 当滑轮向上滚动时
this.upScroll()
}
if (e.wheelDelta < 0) {
// 当滑轮向下滚动时
this.downScroll()
}
} else if (e.detail) {
// Firefox滑轮事件
if (e.detail > 0) {
// 当滑轮向上滚动时
this.upScroll()
}
if (e.detail < 0) {
// 当滑轮向下滚动时
this.downScroll()
}
}
}
},
mounted() {
// 开启监听鼠标滚轮
window.addEventListener('mousewheel', this.wheelScroll) ||
window.addEventListener('DOMMouseScroll', this.wheelScroll);
},
directives: {
// 元素拖拽自定义指令
drag: function(el) {
let dragBox = el;
dragBox.onmousedown = e => {
// 算出鼠标相对元素的位置
let disX = e.clientX - dragBox.offsetLeft;
let disY = e.clientY - dragBox.offsetTop;
document.onmousemove = e => {
// 用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
let left = e.clientX - disX;
let top = e.clientY - disY;
// 移动当前元素
dragBox.style.left = left + "px";
dragBox.style.top = top + "px";
};
document.onmouseup = e => {
// 鼠标弹起来的时候不再移动
document.onmousemove = null;
// 预防鼠标弹起来后还会循环(即预防鼠标放上去的时候还会移动)
document.onmouseup = null;
};
};
}
}
};
</script>
style层,CV即用。
.picture-viewer-box {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
z-index: 999;
background: rgba(0, 0, 0, 0.4);
display: flex;
align-items: center;
justify-content: center
}
.picture-viewer-box .main-body {
width: 60vw;
height: 70vh;
display: flex;
flex-direction: column;
}
.picture-viewer-box .content {
width: 100%;
height: 100%;
flex: 1;
position: relative;
}
.picture-viewer-box .content img {
display: inline-block;
/* width: 100%; */
height: 100%;
object-fit: contain;
cursor: pointer;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0;
}
.picture-viewer-box .content .imgAnimation {
animation: SHOW_IMAGE .3s forwards;
}
@keyframes SHOW_IMAGE {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.picture-viewer-box .content .add-loading {
width: 20px;
height: 20px;
padding: 10px;
border-radius: 10px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
box-sizing: content-box;
background: #fff;
opacity: 1;
}
.picture-viewer-box .content .switchover-img {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
font-size: 20px;
position: absolute;
/* border-radius: 50%; */
cursor: pointer;
font-weight: bold;
}
.picture-viewer-box .content .switchover-img:hover {
color: #15C994;
}
.picture-viewer-box .content .switchover-img.icon-right {
right: 0;
top: 50%;
margin-top: -20px;
}
.picture-viewer-box .content .switchover-img.icon-left {
left: 0;
top: 50%;
margin-top: -20px;
}
.viewer-box {
width: 100%;
height: 50px;
/* background: #fff; */
padding: 6px 0;
text-align: center;
box-sizing: content-box;
z-index: 1;
cursor: pointer;
}
.viewer-box .list {
width: 50px;
height: 50px;
display: inline-block;
vertical-align: top;
margin-right: 10px;
position: relative;
}
.viewer-box .list img {
height: 100%;
object-fit: cover;
}
.viewer-box .list:last-child {
margin-right: 0;
}
.viewer-box .list .viewer-mask {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
transition: all .3s;
background-color: rgba(0, 0, 0, 0.4);
}
.viewer-box .list .viewer-mask:hover {
background-color: transparent;
}
.rotate-box {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
color: #fff;
font-size: 24px;
}
.unselectable {
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
user-select: none;
}
</style>