cavas实现,大致😒写了一半,主要问题就是模糊需要处理,后边没写了,有大神改改的话请@我抄抄,在此谢过了🙏
实现的也不是很好,比较垃圾。。。
注意:
(1)代码mark框初始状态应该是框起来一整个小图,我没改
(2)每次拖拽大图需要重新绘制相应小图的内容到大图展示上,每次canvas重新绘制会模糊,这块没写
(3)小图展示的是1080*1920等比10倍缩小的,根据实际情况可以改为视口等比缩小
(4)大图和小图模糊处理可以通过以下代码处理
img.onload = function () {
// 1:计算大图的展示比例“
// 保持图像的原始尺寸
const imgWidth = img.width;
const imgHeight = img.height;
// 设置画布尺寸为图像尺寸
canvasBig.width = imgWidth;
canvasBig.height = imgHeight;
// 绘制图像
ctxBig.drawImage(img, 0, 0, imgWidth, imgHeight);
// 2:计算小图的展示比例
// 计算等比例缩放
const canvasWidth = canvasSmall.width;
const canvasHeight = canvasSmall.height;
let drawWidth, drawHeight;
if (imgWidth / canvasWidth > imgHeight / canvasHeight) {
drawWidth = canvasWidth;
drawHeight = (imgHeight / imgWidth) * canvasWidth;
} else {
drawHeight = canvasHeight;
drawWidth = (imgWidth / imgHeight) * canvasHeight;
}
// 计算居中位置
const offsetX = (canvasWidth - drawWidth) / 2;
const offsetY = (canvasHeight - drawHeight) / 2;
// 绘制图片
ctxSmall.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
};
showImg.vue代码,就一个文件
<template>
<div class="image-viewer">
<div class="big">
<canvas class="canvas-big" id="canvas-big">
<!--<img src="@/assets/showImg.jpg" alt="" />-->
</canvas>
</div>
<div class="small">
<div class="mask" id="mask" draggable="true"></div>
<canvas class="canvas-small" id="canvas-small"> </canvas>
</div>
</div>
</template>
<script>
import { _debounce, _throttle } from "../util/throttle";
export default {
data() {
return {
imageSrc: "showImg.jpg",
initialMaskSize: { width: 96, height: 54 }, // 初始 mask 大小
maskSize: 54, // 初始 mask 大小
aspectRatio: 96 / 54, // 计算初始纵横比
};
},
mounted() {
let _this = this;
window.onload = function () {
// 获取 canvas 元素
//==mask方框拖拽事件===
const canvasBig = document.getElementById("canvas-big");
const canvasSmall = document.getElementById("canvas-small");
const ctxBig = canvasBig.getContext("2d");
const ctxSmall = canvasSmall.getContext("2d");
// 创建一个新的图像对象
const img = new Image();
// 图像加载完成后绘制图像
img.onload = function () {
ctxBig.drawImage(img, 0, 0, canvasBig.width, canvasBig.height);
ctxSmall.drawImage(img, 0, 0, canvasSmall.width, canvasSmall.height);
};
// 设置图像源(可以是本地路径或远程 URL)
img.src = require("@/assets/showImg.jpg");
const mask = document.getElementById("mask");
const container = document.querySelector(".small");
const containerRect = container.getBoundingClientRect();
const maskRect = mask.getBoundingClientRect();
let isDragging = false;
let offsetX, offsetY;
// 计算 Canvas 的位置
function getCanvasPosition() {
const rect = canvasSmall.getBoundingClientRect();
return {
x: rect.left - containerRect.left,
y: rect.top - containerRect.top,
};
}
// 处理拖拽开始事件
mask.addEventListener("dragstart", (event) => {
event.dataTransfer.setData("text/plain", ""); // 需要设置 dataTransfer 对象
isDragging = true;
const rect = mask.getBoundingClientRect();
offsetX = event.clientX - rect.left;
offsetY = event.clientY - rect.top;
});
// 处理拖拽结束事件
mask.addEventListener("dragend", (event) => {
isDragging = false;
const canvasPos = getCanvasPosition();
let x = event.clientX - containerRect.left - offsetX;
let y = event.clientY - containerRect.top - offsetY;
// 限制拖拽范围在 container 内部
x = Math.max(0, Math.min(x, containerRect.width - maskRect.width));
y = Math.max(0, Math.min(y, containerRect.height - maskRect.height));
mask.style.left = `${x}px`;
mask.style.top = `${y}px`;
// 更新 canvas-big 上的图像显示区域
_this.updateBigCanvasView();
});
// 处理拖拽移动事件
document.addEventListener("dragover", (event) => {
if (isDragging) {
event.preventDefault(); // 必须要阻止默认行为
// const x = event.clientX - containerRect.left - offsetX;
// const y = event.clientY - containerRect.top - offsetY;
// // 限制拖拽范围在 container 内部
// const constrainedX = Math.max(
// 0,
// Math.min(x, containerRect.width - maskRect.width)
// );
// const constrainedY = Math.max(
// 0,
// Math.min(y, containerRect.height - maskRect.height)
// );
// mask.style.left = `${constrainedX}px`;
// mask.style.top = `${constrainedY}px`;
// // 更新 canvas-big 上的图像显示区域
// _this.updateBigCanvasView();
}
});
document.addEventListener("drop", (event) => {
if (isDragging) {
event.preventDefault();
const x = event.clientX - containerRect.left - offsetX;
const y = event.clientY - containerRect.top - offsetY;
// 限制拖拽范围在 container 内部
const constrainedX = Math.max(
0,
Math.min(x, containerRect.width - maskRect.width)
);
const constrainedY = Math.max(
0,
Math.min(y, containerRect.height - maskRect.height)
);
mask.style.left = `${constrainedX}px`;
mask.style.top = `${constrainedY}px`;
// 更新 canvas-big 上的图像显示区域
_this.updateBigCanvasView();
}
});
//====
// 处理 canvas-big 的拖拽事件====
let isDraggingTest = false;
let offsetXTest, offsetYTest;
let offsetXBig = 0,
offsetYBig = 0;
canvasBig.addEventListener("mousedown", (event) => {
isDraggingTest = true;
offsetXTest = event.clientX - offsetXBig;
offsetYTest = event.clientY - offsetYBig;
});
canvasBig.addEventListener(
"mousemove",
_throttle((event) => {
if (isDraggingTest) {
const x = event.clientX - offsetXBig - offsetXTest;
const y = event.clientY - offsetYBig - offsetYTest;
// 更新 mark 的位置
// maskRect.left + x / 10 / 2
console.log(mask.style.left, mask.style.top);
const matchLeft = mask.style.left
? mask.style.left.match(/-?\d+(\.\d+)?/)
: [0];
const matchTop = mask.style.top
? mask.style.top.match(/-?\d+(\.\d+)?/)
: [0];
mask.style.left = `${parseFloat(matchLeft[0], 10) + x / 10 / 2}px`;
mask.style.top = `${parseFloat(matchTop[0], 10) + y / 10 / 2}px`;
offsetXBig = parseFloat(matchLeft[0], 10) || 0;
offsetYBig = parseFloat(matchTop[0], 10) || 0;
_this.updateBigCanvasView();
}
}, 100)
);
canvasBig.addEventListener("mouseup", () => {
isDraggingTest = false;
});
//====
};
//ctrl+滚轮控制mask方框放大缩小======
const small = document.querySelector(".small");
// 绑定事件处理函数到组件实例上
this.resizeMask = (event) => {
if (!event.ctrlKey) return;
event.preventDefault();
const delta = event.deltaY < 0 ? 10 : -10;
// 计算新的大小
let newSize = Math.max(
10,
Math.min(
this.maskSize + delta,
Math.min(small.clientWidth, small.clientHeight)
)
);
// 保持纵横比
let newWidth, newHeight;
if (small.clientWidth / small.clientHeight > this.aspectRatio) {
newWidth = newSize;
newHeight = newSize / this.aspectRatio;
} else {
newHeight = newSize;
newWidth = newSize * this.aspectRatio;
}
// 确保新宽高不会超出容器的范围
newWidth = Math.min(newWidth, small.clientWidth);
newHeight = Math.min(newHeight, small.clientHeight);
this.maskSize = newSize;
this.updateMaskSize(newWidth, newHeight);
this.updateBigCanvasView();
};
this.updateMaskSize(
this.initialMaskSize.width,
this.initialMaskSize.height
);
document.addEventListener("wheel", this.resizeMask, { passive: false });
window.addEventListener("resize", () => {
const newRect = small.getBoundingClientRect();
// 更新小方块的容器尺寸
// (如果需要,可以在这里添加额外逻辑)
});
// Initialize and load images into canvases
this.loadImageToCanvas(this.imageSrc);
},
methods: {
updateMaskSize(width, height) {
const mask = document.getElementById("mask");
mask.style.width = width + "px";
mask.style.height = height + "px";
},
updateBigCanvasView() {
const mask = document.getElementById("mask");
const small = document.querySelector(".small");
const canvasBig = document.getElementById("canvas-big");
const canvasSmall = document.getElementById("canvas-small");
// Calculate the scale factor and position
const scaleX = canvasBig.width / canvasSmall.width;
const scaleY = canvasBig.height / canvasSmall.height;
// Calculate the position and size in canvas-big coordinates
const maskRect = mask.getBoundingClientRect();
const smallRect = small.getBoundingClientRect();
const maskX = (maskRect.left - smallRect.left) / smallRect.width;
const maskY = (maskRect.top - smallRect.top) / smallRect.height;
const maskWidth = maskRect.width / smallRect.width;
const maskHeight = maskRect.height / smallRect.height;
const ctxBig = canvasBig.getContext("2d");
// Set the viewport on the big canvas
ctxBig.clearRect(0, 0, canvasBig.width, canvasBig.height);
ctxBig.drawImage(
canvasSmall,
maskX * canvasSmall.width,
maskY * canvasSmall.height,
maskWidth * canvasSmall.width,
maskHeight * canvasSmall.height,
0,
0,
canvasBig.width,
canvasBig.height
);
},
loadImageToCanvas(src) {
const canvasBig = document.getElementById("canvas-big");
const canvasSmall = document.getElementById("canvas-small");
const image = new Image();
image.src = src;
image.onload = () => {
canvasSmall.width = image.width;
canvasSmall.height = image.height;
this.ctxSmall.drawImage(image, 0, 0);
canvasBig.width = canvasSmall.width;
canvasBig.height = canvasSmall.height;
this.updateBigCanvasView();
};
},
},
beforeDestroy() {
document.removeEventListener("wheel", this.resizeMask);
},
};
</script>
<style scoped lang="less">
///
.image-viewer {
position: relative;
height: 100%;
width: 100%;
.big {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
.canvas-big {
height: 100%;
width: 100%;
object-fit: contain;
}
}
.small {
position: absolute;
bottom: 10px;
right: 10px;
height: 108px;
width: 192px;
border: 1px solid #ccc;
overflow: hidden;
.mask {
z-index: 33;
position: absolute;
top: 0;
left: 0;
height: 54px;
width: 96px;
}
.mask::before {
content: "";
position: absolute;
top: 0; /* 内边距的颜色距离边框的距离 */
left: 0;
right: 0;
bottom: 0;
border: 2px solid #ff0000;
z-index: -1; /* 将伪元素放置在内容下方 */
}
.canvas-small {
height: 100%;
width: 100%;
object-fit: contain;
}
}
}
</style>