需求是大图预览禁止循环,开始是用的elementui里面的el-image组件 但是这个组件源码里面的infinite为true是写死的 意味着必定会循环 我也没有找到其他配置方法可以禁止循环。
自定义大图预览组件:
<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="400px"
height="680px"
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>{{ 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: {
type: Array,
default: () => []
}
},
data() {
return {
out: true,
img: "", // 图片路径
odiv: null,
transform: {
scale: 1,
degree: 0
},
nextIndex: 0, //预览的当前下标
isShow: true
};
},
created() {
// console.log(this.images, this.currentIndex, this.nextIndex, "66666666666666");
if (this.images && this.images.length > 0) {
this.img = this.images[this.nextIndex];
}
},
watch: {
images(newVal) {
this.img = newVal;
}
},
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];
} else {
this.nextIndex = this.images.length - 1;
this.img = 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];
} else {
this.nextIndex = 0;
this.img = 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);
}
timer = setTimeout(() => {
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), 500); // 这里延迟是为了执行动画
},
// 点击下载
// 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;
}
}
};
</script>
<style lang="scss" scoped>
.mark {
z-index: 1000;
position: fixed;
background-color: rgba(3, 3, 3, 1);
opacity: 0.5;
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: 26px;
}
.close_btn {
position: fixed;
top: 15px;
right: 15px;
}
.img_footer {
z-index: 1051;
position: fixed;
// height: 30px;
width: 100%;
background-color: #0a0a0a;
bottom: 0;
left: 0;
}
.img_options {
width: 100%;
height: 28px;
background-color: rgba(241, 241, 241, 0.25);
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
}
.img_options i {
width: 20px;
height: 20px;
margin: 0 10px;
transition: all 0.5s;
}
.img_options i:hover {
color: #ffffff;
font-size: 28px;
cursor: pointer;
}
.pages {
z-index: 1050;
position: fixed;
bottom: 1.1rem;
left: 0;
height: 60px;
width: 100px;
font-size: 26px;
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: 40px;
height: 100%;
font-size: 26px;
color: #ffffff;
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>
父组件中引入:
<template>
<div class="subjective container">
<div class="answer_content">
<span
class="answer_item"
v-for="(item, index) in List"
:key="index"
v-if="index < List.length - 1"
>
<span class="name">
<span>{{ item.classNo }}班</span>
<span>{{ item.name }}</span>
<span>得分:{{ item.score }}</span>
</span>
<img :src="item.pic[0]" alt="" class="images" @click="openShowImage(index, item)" />
</span>
</div>
<!-- 自定义图片预览组件 -->
<showImg v-if="isShow" @listenChild="listenChild" :images="images[currentIndex]"></showImg>
</div>
</template>
<script>
import showImg from "./showImage.vue";
import { getSubjectiveDetail } from "@/api/precisionTeaching/examPaper.js";
export default {
components: { showImg },
name: "",
data() {
return {
examList: [],
List: [],
currentIndex: "",
images: [],
isShow: false
};
},
created() {},
computed: {
classSeq() {
return this.$store.state.user.current_class.classSeq;
},
grade() {
return this.$store.state.user.current_class.grade;
},
subjectName() {
return this.$store.state.user.currentSubject.subjectName;
},
schoolUid() {
return this.$store.state.user.teacherInfo.schoolVo.schoolUid;
},
watchData() {
const { classSeq, grade, subjectName, schoolUid } = this;
return { classSeq, grade, subjectName, schoolUid };
}
},
watch: {
watchData: {
handler() {
this.getExcellentEssay();
},
deep: true
}
},
mounted() {
this.getExcellentEssay();
},
methods: {
// 优秀作答
getExcellentEssay() {
getSubjectiveDetail({
schoolUid: this.schoolUid,
grade: this.grade,
classNo: this.classSeq,
lesson: this.subjectName
}).then(res => {
this.examList = res.data.content;
this.List = this.examList.map(v => {
return {
classNo: v.stuclass,
name: v.stuname,
score: v.score,
pic: v.pic.split(";").filter(e => e !== "")
};
});
this.images = this.List.map(item => {
return item.pic;
});
console.log(this.images, " this.images");
});
},
// 监听子组件事件
listenChild(e) {
// console.log(e, "e");
this.isShow = e;
},
// 点击学生作答 打开预览
openShowImage(index, item) {
this.currentIndex = index;
this.isShow = true;
}
}
};
</script>
<style lang="scss" scoped>
.subjective {
padding: 0 30px 30px 30px;
// height: 800px;
background-color: #fff;
.answer_content {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
margin-top: 16px;
width: 100%;
.answer_item {
display: flex;
margin-right: 60px;
margin-bottom: 28px;
flex-direction: column;
justify-content: center;
&:nth-child(3n) {
margin-right: 0;
}
.name {
margin-bottom: 5px;
color: #333333;
font-weight: 700;
font-size: 18px;
span {
margin-right: 4px;
}
}
}
.images {
width: 300px;
height: 400px;
}
}
}
</style>