提示:本文是在vue2.0的项目创建的,同时使用了element ui的icon图标
我在网上找了挺久,但是没有找到几个满意的案例,于是决定自己手写了一个.
当然嫌麻烦的同学可以参考viewer这个插件
传送门:
提示:这篇文章是我随意找了一篇贴的,如有侵权请联系我删除:
在vue中使用viewer插件
viewer中文文档
以下的代码如有可以改进的地方,还希望大家在评论区里留言或私信告诉我,一起互相学习交流。
新建一个showImage.vue,代码如下:
1、项目使用到了element ui组件,例如文字提示框el-tooltip,图标等,如果直接cv引用代码,还要先安装element ui,这里贴一个传送门:
element中文文档
2、要提一下的是,还使用到了 animate.css 动画,不了解的同学可以看看官方文档
aniamte.css中文文档也可以看看我上一篇文章的第二点 —— 引入animate.css动画
vue使用高德地图
<template>
<div style="position: relative; overflow: hidden">
<!-- 遮罩层-->
<div class="mark" style=""></div>
<!-- 主体部分-->
<div
class="img_box animated"
:class="out ? 'zoomIn' : 'zoomOut'"
@mousewheel="debounce(handleScroll($event), 200)"
>
<!-- 右上角关闭按钮 -->
<div class="close" @click="closeMark">
<i class="el-icon-close close_btn"></i>
</div>
<!-- 图片 -->
<div style="margin: auto; position: relative">
<img
width="100%"
height="100%"
class="img"
:style="imgStyle"
@mousedown.prevent="dropImage"
:src="img"
@keyup.left="handleActions('left')"
@keyup.right="handleActions('right')"
/>
</div>
<!-- 上一张 -->
<div class="prev" @mouseover="isShow = true" @mouseleave="isShow = false">
<i
v-if="isShow"
class="el-icon-arrow-left prev_img"
@click="prevImg(2)"
></i>
</div>
<!-- 下一张-->
<div class="next" @mouseover="isShow = true" @mouseleave="isShow = false">
<i
v-if="isShow"
class="el-icon-arrow-right next_img"
@click="nextImg(2)"
></i>
</div>
</div>
<!-- 图片名字及页数-->
<div class="pages animated" :class="out ? 'flipInX' : 'zoomOutLeft'">
<div class="image_name">{{ imageName }}</div>
<div>{{ nextIndex + 1 }} / {{ images.length }}</div>
</div>
<!-- 底部操作区-->
<div class="img_footer animated" :class="out ? 'flipInX' : 'zoomOutLeft'">
<div class="img_options">
<el-tooltip
content="图片比例1:1"
:open-delay="500"
placement="bottom"
effect="light"
>
<i
@click="handleActions('restore')"
class="el-icon-c-scale-to-original"
></i>
</el-tooltip>
<el-tooltip
content="放大图片"
:open-delay="500"
placement="bottom"
effect="light"
>
<i @click="handleActions('zoomIn')" class="el-icon-zoom-in"></i>
</el-tooltip>
<el-tooltip
content="缩小图片"
:open-delay="500"
placement="bottom"
effect="light"
>
<i @click="handleActions('zoomOut')" class="el-icon-zoom-out"></i>
</el-tooltip>
<el-tooltip
content="逆时针90度"
:open-delay="500"
placement="bottom"
effect="light"
>
<i
@click="handleActions('refresh-left')"
class="el-icon-refresh-left"
></i>
</el-tooltip>
<el-tooltip
content="顺时针90度"
:open-delay="500"
placement="bottom"
effect="light"
>
<i
@click="handleActions('refresh-right')"
class="el-icon-refresh-right"
></i>
</el-tooltip>
<el-tooltip
content="第一张"
:open-delay="500"
placement="bottom"
effect="light"
>
<i class="el-icon-d-arrow-left" @click="prevImg(1)"></i>
</el-tooltip>
<el-tooltip
content="上一张"
:open-delay="500"
placement="bottom"
effect="light"
>
<i class="el-icon-arrow-left" @click="prevImg(2)"></i>
</el-tooltip>
<el-tooltip
content="下一张"
:open-delay="500"
placement="bottom"
effect="light"
>
<i class="el-icon-arrow-right" @click="nextImg(2)"></i>
</el-tooltip>
<el-tooltip
content="最后一张"
:open-delay="500"
placement="bottom"
effect="light"
>
<i class="el-icon-d-arrow-right" @click="nextImg(1)"></i>
</el-tooltip>
<el-tooltip
content="下载图片"
:open-delay="500"
placement="bottom"
effect="light"
>
<i class="el-icon-download" @click="down"></i>
</el-tooltip>
<el-tooltip
content="关闭预览"
:open-delay="500"
placement="bottom"
effect="light"
>
<i class="el-icon-circle-close" @click="closeMark"></i>
</el-tooltip>
</div>
</div>
</div>
</template>
<script>
export default {
props: ["images"],
data() {
return {
out: true,
img: "", // 图片路径
odiv: null,
transform: {
scale: 1,
degree: 0,
},
nextIndex: 0, //当前下标
imageName: "", // 图片名字
isShow: false,
};
},
created() {
if (this.images && this.images.length > 0) {
this.imageName = this.images[this.nextIndex];
this.img = this.images[this.nextIndex];
}
},
computed: {
// 是否是第一张
isFirst() {
return this.nextIndex === 0;
},
// 是否是最后一张
isLast() {
return this.nextIndex === this.images.length - 1;
},
// 用于计算图片缩小放大样式
imgStyle() {
const { scale, degree } = this.transform;
const style = {
transform: `scale(${scale}) rotate(${degree}deg)`,
};
return style;
},
},
methods: {
nextImg(type) {
// 点击切换下一张
if (this.images.length && this.images.length > 1) {
if (this.isLast) {
this.$message("这是最后一张啦");
} else {
if (type === 2) {
this.nextIndex += 1;
this.img = this.images[this.nextIndex];
this.imageName = this.images[this.nextIndex];
} else {
this.nextIndex = this.images.length - 1;
this.img = this.images[this.nextIndex];
this.imageName = this.images[this.nextIndex];
}
this.reset();
}
} else {
this.$message({
type: "error",
message: "错误预览!",
});
}
},
prevImg(type) {
// 点击切换上一张
if (this.images.length && this.images.length > 1) {
if (this.isFirst) {
this.$message("这就是第一张啦");
} else {
if (type === 2) {
this.nextIndex -= 1;
this.img = this.images[this.nextIndex];
this.imageName = this.images[this.nextIndex];
} else {
this.nextIndex = 0;
this.img = this.images[this.nextIndex];
this.imageName = this.images[this.nextIndex];
}
this.reset();
}
} else {
this.$message({
type: "error",
message: "错误预览!",
});
}
},
// 防抖,需要的自己调用以下 fn是要执行的函数,delay是延迟时间默认500ms
debounce(fn, delay = 500) {
// 是闭包中的
let timer;
// 事件调用的函数,相当于obj调用函数
return function () {
// 这个if 判断不做也没关系,可直接清空,只有第一次timer非空
if (timer) {
clearTimeout(timer);
}
// 此时的箭头函数的this 和 arguments 都是从外部函数继承而来
// r如果用普通函数就要用词法作用域 var thsta = this var arg = arguments
timer = setTimeout(() => {
// 使得传入的回调函数的this 指向Input这个元素对象
// arguments是该事件的详情,可以获得该函数被调用时的所有参数,是一个event 对象(所有Dom事件都会传event对象进入)
// 直接使用 fn() 问题也不大
fn.apply(this, arguments);
timer = null;
}, delay);
};
},
reset() {
// 初始化图片样式
this.transform = {
scale: 1,
degree: 0,
};
},
// 拖拽图片
dropImage(e) {
if (e === null) {
return;
}
this.odiv = e.target; //获取目标元素
//算出鼠标相对元素的位置
let disX = e.clientX - this.odiv.offsetLeft;
let disY = e.clientY - this.odiv.offsetTop;
document.onmousemove = (s) => {
//鼠标按下并移动的事件
//用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
let left = s.clientX - disX;
let top = s.clientY - disY;
//移动当前元素
this.odiv.style.left = left + "px";
this.odiv.style.top = top + "px";
};
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
};
},
// 关闭预览
closeMark() {
this.out = false;
setTimeout(() => this.$emit("listenChild", false), 1000); // 这一秒延迟是为了执行动画
},
down() {
let url = this.images[this.nextIndex];
this.download(url,name);
},
// 下载 这里是做了非同源跨域处理,关于下载待改进的地方还有很多,大家不要吝啬发言哇!
download(imageSrc,imgName) {
let image = new Image();
// 解决跨域 Canvas 污染问题
image.setAttribute("crossOrigin", "anonymous");
image.onload = function () {
let canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
let context = canvas.getContext("2d");
context.drawImage(image, 0, 0, image.width, image.height);
let url = canvas.toDataURL("image/png"); //得到图片的base64编码数据
let a = document.createElement("a"); // 生成一个a元素
let event = new MouseEvent("click"); // 创建一个单击事件
a.download = imgName || "photo"; // 设置图片名称
a.href = url; // 将生成的URL设置为a.href属性
a.dispatchEvent(event); // 触发a的单击事件
};
image.src = imageSrc;
},
//判断滚动缩放
handleScroll(e) {
let direction = e.deltaY > 0 ? "down" : "up"; //deltaY为正则滚轮向下,为负滚轮向上
if (direction === "down" && e.deltaY >= 100) {
//125为用户一次滚动鼠标的wheelDelta的值
this.handleActions("zoomOut");
}
if (direction === "up" && e.deltaY <= -100) {
this.handleActions("zoomIn");
}
},
// 旋转放大缩小
handleActions(action) {
// 放大旋转等操作
const { zoomRate, rotateDeg, enableTransition } = {
zoomRate: 0.2,
rotateDeg: 90,
enableTransition: true,
};
const { transform } = this;
switch (action) {
case "zoomOut":
if (transform.scale > 0.2) {
transform.scale = parseFloat(
(transform.scale - zoomRate).toFixed(3)
);
}
break;
case "zoomIn":
if (transform.scale < 7) {
transform.scale = parseFloat(
(transform.scale + zoomRate).toFixed(3)
);
}
break;
case "refresh-left":
transform.degree -= rotateDeg;
break;
case "refresh-right":
transform.degree += rotateDeg;
break;
case "restore":
// 恢复1:1
transform.scale = 1;
break;
}
transform.enableTransition = enableTransition;
},
},
watch: {
images(newVal) {
this.img = newVal;
},
},
};
</script>
<style scoped>
.mark {
z-index: 1000;
position: fixed;
background-color: rgba(3, 3, 3, 0.6);
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.img_box {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1050;
display: flex;
}
.img {
position: relative;
}
.close {
position: fixed;
width: 60px;
height: 60px;
z-index: 1060;
top: 0;
right: 0;
font-size: 0.3rem;
color: rgba(255, 255, 255, 0.8);
background-color: rgba(38, 35, 35, 0.8);
border-radius: 0 0 0 100%;
cursor: pointer;
transition: all 0.5s;
}
.close:hover {
width: 80px;
height: 80px;
font-size: 0.4rem;
}
.close_btn {
position: fixed;
top: 15px;
right: 15px;
}
.img_footer {
z-index: 1051;
position: fixed;
height: 1.1rem;
width: 100%;
background-color: #0a0a0a;
bottom: 0;
left: 0;
}
.img_options {
width: 100%;
height: 0.8rem;
background-color: rgba(241, 241, 241, 0.25);
display: flex;
justify-content: center;
align-items: center;
font-size: 0.48rem;
}
.img_options i {
margin: 0 0.2rem;
transition: all 0.5s;
}
.img_options i:hover {
color: #ffffff;
font-size: 0.6rem;
cursor: pointer;
}
.pages {
z-index: 1050;
position: fixed;
bottom: 1.1rem;
left: 0;
height: 0.5rem;
width: 100%;
font-size: 0.3rem;
color: #ffffff;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.image_name {
font-size: 0.18rem;
color: #ffffff;
}
.next,
.prev {
position: fixed;
width: 2.5rem;
height: 100%;
font-size: 0.625rem;
z-index: 1051;
display: flex;
justify-content: center;
align-items: center;
}
.next {
right: 0;
}
.prev {
left: 0;
}
.prev_img,
.next_img {
color: #ffffff;
border: 0.0375rem solid #ffffff;
border-radius: 100%;
transition: all 0.5s;
opacity: 0.3;
}
.prev_img {
left: 12%;
}
.next_img {
right: 12%;
}
.prev_img:hover,
.next_img:hover {
cursor: pointer;
opacity: 1;
}
</style>
新建index.vue文件,代码如下:
<template>
<div>
<button @click="openShowImage">图片预览</button>
<showImg v-if="isShow" @listenChild="listenChild" :images="images"></showImg>
</div>
</template>
<script>
import showImg from "../../../../components/showImg";
export default {
components:{ showImg },
data(){
return {
images: [
"https://qny.samt.top/3ukysgdRM9PP43ET.jpg",
"https://qny.samt.top/2SN2472MG0wpji.jpg"
],
isShow: false
}
},
methods:{
// 监听子组件事件
listenChild(e) {
this.isShow = e;
},
openShowImage() {
this.isShow = true;
},
}
}
</script>
提示:文章到这里就结束了:
以上只是我个人的写法,如有不妥之处还请谅解我这个前端小白,欢迎大家在评论区留言,交流探讨。
对了,后续可能还会继续改进封装,所以大家别走太远哦~ 建议关注+收藏以免迷路。