```
```
<template>
<div class="mainPage">
<div class="watchMap">
<div class="imgBox" ref="maskBox" @mousedown="onmousedownHandle" @wheel="wheelHandle">
<div class="bigBox" ref="bigBox" :style="{
top: top + 'px',
left: left + 'px',
transform: `scale(${scale})`,
}"
@mousemove="handleMouseMoveBigBox"
>
<!-- 如果只是展示一张拼接大图,想要实现放大缩小可以直接使用transform这个属性-->
<img
@mousemove="handleMouseMoveImg(item)"
v-for="item in imgList"
:key="item.src"
:data-src="item.src"
@load="onImageLoad"
:x="item.x"
:y="item.y"
:ref="getRef(item)"
alt=""
:style="{
top: imgH * item.y + 'px',
left: imgW * item.x + 'px',
}"
>
<div
class="thumbnailMarker"
:style="{ top: imgBoxY + 'px', left: imgBoxX + 'px' }"
></div>
</div>
</div>
<div class="thumbnailBox" ref="thumbnailBox">
<img
:src="thumbnailSrc"
class="thumbnail"
@click="handleThumbnailMouseMove"
/>
<!-- @mousemove.stop="handleThumbnailMouseMove"-->
<div
class="thumbnailMarker"
:style="{ top: thumbnailMarkerTop + 'px', left: thumbnailMarkerLeft + 'px' }"
></div>
</div>
</div>
<div class="footerModal">
<div class="bottom-left">
最小倍率:{{scaleList[0]}}
最大倍率:{{ scaleList[scaleList.length - 1]}}
<a style="display: flex;align-items: center">
放大<i class="el-icon-zoom-in" @click="imgScaleHandle(+1);isMouseScroll = false"></i>
当前倍率:{{scaleList[size]}}
缩小<i class="el-icon-zoom-out" @click="imgScaleHandle(-1);isMouseScroll = false"></i>
</a>
</div>
<div class="bottom-right">
<button
style="
width: 98px;
height: 34px;
border: 1px solid #00a8e3;
border-radius: 19px;
background-color: white;
color: #00a8e3;
cursor: pointer;
margin-right: 20px;
"
@click="clickScreen">截图</button>
<button
style="
width: 98px;
height: 34px;
background: #00a8e3;
border-radius: 19px;`在这里插入代码片`
border: 1px solid #00a8e3;
color: white;
cursor: pointer;
"
@click="showImgCancel"
v-debounce="1000"
>
关闭
</button>
</div>
</div>
<!-- 截图-->
<screenShot ref="ScreenShot" @confirm="screenShotConfirm"></screenShot>
</div>
</template>
<script>
import screenShot from "@/components/SlideLibrary/ScreenShot.vue";
export default {
components: {screenShot},
props: {
slideId: {
default: 0
}
},
data() {
return {
imgList: [],
imgW: 2048,
imgH: 2048,
deg: 0,
top: 0,
left: 0,
scale: 1,
size: 0,
maxX: 0,
maxY: 0,
mousewheelevt: null,
observer: null,
//
lastSize: 0,// 上一个倍率
mouseX: 0, // 鼠标在当前可视窗口的x
mouseY: 0,// 鼠标在当前可视窗口的y
mouseImgX: 0, // 鼠标点下图片的x
mouseImgY: 0, // 鼠标点下图片的y
maxScale: 0,
itemImgX:0,
itemImgY:0,
imgBoxX: 0,
imgBoxY: 0,
thumbnailSrc: '',
thumbnailMarkerTop: 0,
thumbnailMarkerLeft: 0,
thumbnailWidth: 100, // 根据您的缩略图大小进行调整
thumbnailHeight: 100, // 根据您的缩略图大小进行调整
bigImgWidth: 0, // 整个玻片的宽
bigImgHeight: 0, // 整个玻片的高
smallImgWidth: 0, // 最小层玻片整个玻片的宽
smallImgHeight: 0, // 最小层玻片整个玻片的高
multiple: 0, // 倍率
maskBoxX: 0,// maskBoxs上的坐标
maskBoxY: 0,// maskBoxs上的坐标
scrollTimeout: null,
isMouseScroll: false,
scaleList: [], // 比例列表
};
},
computed: {
},
async mounted() {
await this.getImgList(this.size)
// 兼容火狐浏览器
this.mousewheelevt = /Firefox/i.test(navigator.userAgent)
? "DOMMouseScroll"
: "mousewheel";
// 为空间区域绑定鼠标滚轮事件 =》 处理函数是wheelHandle
// 如果你监听了window的scroll或者touchmove事件,你应该把passive设置为true,这样滚动就会流畅很多
// this.$refs.maskBox.addEventListener(this.mousewheelevt, this.wheelHandle);
this.flushedData()
// 监听鼠标移动事件
this.$refs.maskBox.addEventListener('mousemove', this.onMouseMove);
//初始化图片
this.initImage();
},
beforeDestroy() {
//取消监听
this.$refs.maskBox.removeEventListener(
this.mousewheelevt,
this.wheelHandle
);
},
created() {
this.handleReset();
},
methods: {
// 比例列表
generateSequence(n) {
let value = 1; // 初始值为 1
let sequence = [];
for (let i = 0; i < n; i++) {
sequence.push(value);
value *= this.multiple; // 每项是前一项的 multiple(3)倍
}
this.scaleList = sequence
},
screenShotConfirm () {
},
// 点击截图
clickScreen () {
// 点击截图功能
this.$refs.ScreenShot.btnClick()
},
// 展示玻片弹窗
showImgCancel () {
this.$emit('close')
// this.slideImgShow = false
},
// 点击缩略图
handleThumbnailMouseMove(event) {
// 计算鼠标相对于缩略图框的位置
const rect = this.$refs.thumbnailBox.getBoundingClientRect();
const mouseX = event.clientX - rect.left;
const mouseY = event.clientY - rect.top;
this.$nextTick(() => {
// 更新缩略图标记位置
this.thumbnailMarkerLeft = mouseX - 3
this.thumbnailMarkerTop = mouseY - 3
// 计算相对于图像的位置(考虑比例) 更新大图上标记位置
this.imgBoxX = mouseX / this.thumbnailWidth * this.bigImgWidth;
this.imgBoxY = mouseY / this.thumbnailHeight * this.bigImgHeight;
// 计算大图在视口中心的偏移量
const maskBoxRect = this.$refs.maskBox.getBoundingClientRect();
const offsetX = maskBoxRect.width / 2;
const offsetY = maskBoxRect.height / 2;
this.left = -this.imgBoxX + offsetX
this.top = -this.imgBoxY + offsetY
})
// console.log(mouseX,mouseY);
},
getRef(item) {
return `image-${item.src.replace(/[^a-zA-Z0-9]/g, '')}`;
},
// 划过div 包裹图片div的操作
handleMouseMoveBigBox (event) {
const bigBoxRect = this.$refs.bigBox.getBoundingClientRect();
const mouseX = event.clientX - bigBoxRect.left;
const mouseY = event.clientY - bigBoxRect.top;
// 在整个大图上的下x,y坐标
this.imgBoxX = mouseX
this.imgBoxY = mouseY
// console.log(this.imgBoxX,this.imgBoxY,"this.imgBoxX,this.imgBoxY在handleMouseMoveBigBox中");
this.$nextTick(() => {
// 更新标记位置
// 计算缩略图上标记的位置
const thumbnailBoxRect = this.$refs.thumbnailBox.getBoundingClientRect();
const thumbnailWidth = thumbnailBoxRect.width;
const thumbnailHeight = thumbnailBoxRect.height;
const realWidth = this.bigImgWidth
const realHeight = this.bigImgHeight
// 假设缩略图和大图的宽高比例相同,可以直接按比例计算
this.thumbnailMarkerLeft = (this.imgBoxX / realWidth ) * thumbnailWidth - 4;
this.thumbnailMarkerTop = (this.imgBoxY / realHeight ) * thumbnailHeight - 4;
})
// console.log(`鼠标在 bigBox 中的坐标:(${mouseX}, ${mouseY})`);
// console.log(this.thumbnailMarkerLeft,this.thumbnailMarkerTop,"this.thumbnailMarkerTop");
},
// 划过图片的操作
handleMouseMoveImg(item) {
this.itemImgX = item.x
this.itemImgY = item.y
// console.log('当前鼠标所在位置的图片名称:', item.x+"_"+item.y);
// console.log('自定义字段 x:', item.x, 'y:', item.y);
},
// 进入视野的图片显示
flushedData() {
this.$nextTick(() => {
// 创建 Intersection Observer 实例
this.observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
// console.log(entry);
if (entry.isIntersecting) {
const lazyImage = entry.target;
if (lazyImage.dataset.src) {
console.log('图片进入视野:', lazyImage.dataset.src);
lazyImage.src = lazyImage.dataset.src;
this.observer.unobserve(lazyImage); // 加载后取消观察,避免重复加载
} else {
console.error('图片数据源(data-src)为空:', lazyImage);
}
}
});
}, {
root: this.$refs.maskBox,
rootMargin: '2048px 100px 2048px 2048px', // 根据需要调整
threshold: 0.1 // 调整阈值数组以适应不同的可见性级别
});
// 观察所有带有 data-src 属性的图片
const images = this.$refs.maskBox.querySelectorAll('img');
images.forEach(img => {
if (img.dataset.src) {
this.observer.observe(img);
} else {
console.error('图片缺少数据源(data-src)属性:', img);
}
});
})
},
// 图片加载
onImageLoad() {
},
async getImgList (multiple) {
if (this.slideId) {
this.$axios({
method: "POST",
url: "请求地址",
data: {
multiple: multiple || this.size
},
}).then((res) => {
console.log(res.data.data);
if (res.data.resultFlag == true) {
if (res.data.data.slideMapList.length > 0) {
let data = res.data.data.slideMapList
this.maxScale = data[0].allhierarchy // 总层级: 一共有几层
this.multiple = data[0].multiple // 图片上传时的倍率比如图片按照3*3来生成
this.fromData(data)
if (multiple == 0) {
this.thumbnailSrc = res.data.data.slideMapList[0].imgUrl
// 获取比例列表
this.generateSequence(this.maxScale)
}
}
} else {
this.$message({
message: res.data.msg,
type: 'error',
customClass: 'mzindex'
})
}
});
}
},
// 获取图片
async fromData(listArr) {
try {
if (this.size == 0) {
const promises = listArr.map(async item => {
return new Promise((resolve, reject) => {
let img = new Image();
img.onload = () => {
let obj = {
src: item.imgUrl,
x: item.rowX,
y: item.rowY,
width: img.naturalWidth,
height: img.naturalHeight
};
resolve(obj);
};
img.onerror = (error) => {
reject(error);
};
img.src = item.imgUrl;
});
});
this.imgList = await Promise.all(promises);
} else {
this.imgList = []
listArr.forEach(item => {
let obj = {
src: item.imgUrl,
x: item.rowX,
y: item.rowY,
// width: img.naturalWidth,
// height: img.naturalHeight
};
this.imgList.push(obj)
})
}
// console.log(this.imgList);
this.maxX = Math.max(...this.imgList.map(curr => curr.x), 0);
this.maxY = Math.max(...this.imgList.map(curr => curr.y), 0);
// console.log(this.bigImgWidth,this.bigImgHeight,"没更改前旧宽高");
let oldBigImgWidth = this.bigImgWidth;
let oldBigImgHeight = this.bigImgHeight;
let newWidth = 0;
let newHeight = 0;
if(this.size == 0 && !this.smallImgWidth) {
this.imgList.forEach(curr => {
if (curr.x <= this.maxX && curr.y == 0) {
newWidth += curr.width;
}
if (curr.y <= this.maxY && curr.x == 0) {
newHeight += curr.height;
}
});
this.smallImgWidth = newWidth
this.smallImgHeight = newHeight
} else {
newWidth = this.calculatedValue(this.size, this.multiple, this.smallImgWidth)
newHeight = this.calculatedValue(this.size, this.multiple, this.smallImgHeight)
}
this.bigImgWidth = newWidth;
this.bigImgHeight = newHeight;
this.imgBoxX = this.imgBoxX / oldBigImgWidth * this.bigImgWidth;
this.imgBoxY = this.imgBoxY / oldBigImgHeight * this.bigImgHeight;
let offsetX,offsetY
if (this.isMouseScroll) {
offsetX = this.imgBoxX - this.maskBoxX;
offsetY = this.imgBoxY - this.maskBoxY;
offsetX = offsetX || 0
offsetY = offsetY || 0
// console.log(11,"进入isMouseScroll=============",offsetX,offsetY);
this.left = offsetX * -1
this.top = offsetY * -1
} else {
// console.log(22,"进入2222");
// 计算大图在视口中心的偏移量
const maskBoxRect = this.$refs.maskBox.getBoundingClientRect();
offsetX = maskBoxRect.width / 2;
offsetY = maskBoxRect.height / 2;
this.left = -this.imgBoxX + offsetX
this.top = -this.imgBoxY + offsetY
}
// console.log(this.imgBoxX,this.imgBoxY,"this.imgBoxX,this.imgBoxY在fromData中=============");
// console.log(this.maskBoxX,this.maskBoxY,"this.maskBoxX,this.maskBoxY------------");
this.lastSize = Math.floor(this.size);
await this.flushedData();
} catch (e) {
console.log(e);
}
},
// 计算值
calculatedValue (size,scale,num) {
let value = num * Math.pow(scale, size)
return value
},
/**
* 重置
*/
handleReset() {
// this.imgW = 0;
// this.imgH = 0;
this.top = 0;
this.left = 0;
this.deg = 0;
this.scale = 1;
this.size = 0;
this.initImage();
},
/**
* 初始化图片
*/
async initImage() {
},
// 放大缩小按钮
async imgScaleHandle(zoom) {
this.size += zoom;
if (this.size < 0) {
this.size = 0;
} else if (this.size > this.maxScale - 1) {
this.size = this.maxScale - 1
}
const newRange = Math.floor(this.size);
// console.log(newRange,this.lastSize);
if (newRange !== this.lastSize) {
// this.lastSize = newRange;
await this.getImgList(newRange);
}
},
/**
* 鼠标滚动 实现放大缩小
*/
async wheelHandle(e) {
// 如果已有定时器,清除它
if (this.scrollTimeout) {
clearTimeout(this.scrollTimeout);
}
// 设置一个新的定时器
this.scrollTimeout = setTimeout(() => {
this.isMouseScroll = true
// console.log(this.isMouseScroll,"wheelHandle");
// 滚动结束后的处理逻辑
// 更新缩放比例
// this.scale = newScale;
const ev = e || window.event; // 兼容性处理 => 火狐浏览器判断滚轮的方向是属性 detail,谷歌和ie浏览器判断滚轮滚动的方向是属性 wheelDelta
// dir = -dir; // dir > 0 => 表示的滚轮是向上滚动,否则是向下滚动 => 范围 (-120 ~ 120)
const dir = ev.detail ? ev.detail * -120 : ev.wheelDelta;
let zoom = dir > 0 ? 1 : -1
//滚动的数值 / 2000 => 表示滚动的比例,用此比例作为图片缩放的比例
this.imgScaleHandle(zoom,e);
}, 150); // 延迟时间根据需要调整
},
/**
* 处理图片拖动
*/
onmousedownHandle(e) {
const that = this;
const startX = e.pageX;
const startY = e.pageY;
const startLeft = this.left || 0;
const startTop = this.top || 0;
// 使用箭头函数保持 `this` 的正确指向
this.$refs.maskBox.onmousemove = (el) => {
const ev = el || window.event;
ev.preventDefault();
// 计算移动后的位置
that.left = startLeft + ev.pageX - startX;
that.top = startTop + ev.pageY - startY;
const maskBoxRect = that.$refs.maskBox.getBoundingClientRect();
// 确保位置在容器内
if (that.left + 300 > maskBoxRect.width) {
that.left = maskBoxRect.width - 300;
}
if (that.top + 300 > maskBoxRect.height) {
that.top = maskBoxRect.height - 300;
}
// console.log(that.left,that.top,this.bigImgWidth);
if (that.left - 300 < -this.bigImgWidth) {
that.left = -this.bigImgWidth + 300;
}
if (that.top - 300 < -this.bigImgHeight) {
that.top = -this.bigImgHeight + 300;
}
};
this.$refs.maskBox.onmouseup = () => {
that.$refs.maskBox.onmousemove = null;
that.$refs.maskBox.onmouseup = null;
};
if (e.preventDefault) {
e.preventDefault();
} else {
return false;
}
},
/**
* 鼠标移动事件处理程序,用于获取鼠标位置
*/
onMouseMove(e) {
const rect = this.$refs.maskBox.getBoundingClientRect();
// console.log(e.clientX);
// clientX 表示鼠标事件发生时,鼠标指针相对于浏览器窗口客户区域(即浏览器视口)左边界的水平坐标位置。
// 是 maskBox 元素相对于浏览器窗口客户区域左边界的距离(即 maskBox 的左边缘在视口中的位置)。
let mouseX = e.clientX - rect.left
let mouseY = e.clientY - rect.top
this.maskBoxX = mouseX
this.maskBoxY = mouseY
// console.log(this.maskBoxX, this.maskBoxY,"this.maskBoxX, this.maskBoxY");
// 计算大图在视口中心的偏移量
// const maskBoxRect = this.$refs.maskBox.getBoundingClientRect();
// const offsetX = maskBoxRect.width / 2;
// const offsetY = maskBoxRect.height / 2;
},
// 获取 bigBox 到 maskBox 的距离
getDistanceBetweenBoxes() {
const bigBoxRect = this.$refs.bigBox.getBoundingClientRect(); // 可视窗口
const maskBoxRect = this.$refs.maskBox.getBoundingClientRect(); // 包裹图片div
const distanceX = bigBoxRect.left - maskBoxRect.left;
const distanceY = bigBoxRect.top - maskBoxRect.top;
return { distanceX, distanceY };
},
},
};
</script>
<style lang="less" scoped>
.watchMap {
//width: 1200px;
//height: 662px;
width: 98vw;
height: 90vh;
background: pink;
position: relative;
border: 1px solid #333;
margin: 0 auto;
// overflow: hidden;
//margin-left: 360px;
.bigBox{
position: relative;
//transition: top 0.3s ease, left 0.3s ease; /* 添加平滑过渡效果 */
}
.imgBox {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
img {
cursor: move;
position: absolute;
object-fit: cover;
}
}
.Tools {
width: 43px;
min-height: 100px;
position: absolute;
right: -50px;
top: 0;
user-select: none;
.Tools-item {
width: 100%;
height: 44px;
background: pink;
margin-bottom: 5px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.Tools-item:hover{
background-color: red;
color: #fff;
}
}
}
.thumbnailBox {
position: absolute;
top: 0;
right: 0;
width: 100px;
height: 100px;
border: 1px solid #ccc;
//margin-top: 10px;
}
.thumbnail {
width: 100px;
height: 100px;
}
.thumbnailMarker {
position: absolute;
width: 6px;
height: 6px;
border: 1px solid red;
//border-radius: 50%;
}
.footerModal{
width: 85%;
height: 50px;
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 auto;
.bottom-left{
display: flex;
align-items: center;
}
i{
font-size: 24px;
margin-right: 20px;
}
}
</style>
```
```
vue pc端展示大图片,实现放大缩小移动
最新推荐文章于 2025-03-24 17:32:50 发布