<template>
<div class="canvas-container">
<canvas
ref="canvas"
@wheel.prevent="handleWheel"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
@mouseout="handleMouseUp"
></canvas>
</div>
</template>
<script>
export default {
data() {
return {
imageSrc:'https://mail.iflytek.com/coremail/s?func=lp:getImg&org_id=&img_id=background_001',
margin:0.1,
canvas: null,
ctx: null,
img: null,
scale: 1,
minScale: 0.1,
maxScale: 10,
scaleStep: 0.1,
offsetX: 0,
offsetY: 0,
startX: 0,
startY: 0,
isDragging: false,
initialScale: 1
}
},
watch: {
imageSrc(newVal) {
this.loadImage(newVal);
}
},
mounted() {
this.initCanvas();
this.loadImage(this.imageSrc);
},
methods: {
initCanvas() {
this.canvas = this.$refs.canvas;
this.ctx = this.canvas.getContext('2d');
this.resizeCanvas();
window.addEventListener('resize', this.resizeCanvas);
},
resizeCanvas() {
const container = this.canvas.parentElement;
this.canvas.width = container.clientWidth;
this.canvas.height = container.clientHeight;
if (this.img && this.img.complete) {
this.calculateInitialScale();
this.drawImage();
}
},
loadImage(src) {
this.img = new Image();
this.img.onload = () => {
this.calculateInitialScale();
this.drawImage();
};
this.img.onerror = () => {
console.error('Failed to load image:', src);
};
this.img.src = src;
},
calculateInitialScale() {
if (!this.img || !this.canvas) return;
const canvasAspect = this.canvas.width / this.canvas.height;
const imgAspect = this.img.width / this.img.height;
// 考虑边距的缩放计算
const marginFactor = 1 - Math.min(Math.max(this.margin, 0), 0.9);
if (imgAspect > canvasAspect) {
// 图片比画布更宽,以宽度为基准
this.initialScale = (this.canvas.width * marginFactor) / this.img.width;
} else {
// 图片比画布更高,以高度为基准
this.initialScale = (this.canvas.height * marginFactor) / this.img.height;
}
this.scale = this.initialScale;
this.offsetX = 0;
this.offsetY = 0;
},
drawImage() {
if (!this.img || !this.img.complete || !this.ctx) return;
// 清除画布
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// 计算缩放后的实际尺寸
const scaledWidth = this.img.width * this.scale;
const scaledHeight = this.img.height * this.scale;
// 计算居中位置(考虑偏移量)
const x = (this.canvas.width - scaledWidth) / 2 + this.offsetX;
const y = (this.canvas.height - scaledHeight) / 2 + this.offsetY;
// 保存当前状态
this.ctx.save();
// 设置绘制质量
this.ctx.imageSmoothingEnabled = true;
this.ctx.imageSmoothingQuality = 'high';
// 绘制图像
this.ctx.drawImage(
this.img,
0, 0, this.img.width, this.img.height,
x, y, scaledWidth, scaledHeight
);
// 恢复状态
this.ctx.restore();
},
handleWheel(e) {
e.preventDefault();
// 获取鼠标在canvas上的位置
const rect = this.canvas.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
// 计算鼠标在图像上的位置(相对于图像中心)
const imgX = (mouseX - this.canvas.width / 2 - this.offsetX) / this.scale;
const imgY = (mouseY - this.canvas.height / 2 - this.offsetY) / this.scale;
// 计算缩放方向
const delta = e.deltaY > 0 ? -1 : 1;
// 保存之前的缩放比例
const oldScale = this.scale;
// 更新缩放比例
this.scale += delta * this.scaleStep * this.scale;
this.scale = Math.max(this.minScale, Math.min(this.maxScale, this.scale));
// 计算缩放中心偏移
this.offsetX -= imgX * (this.scale - oldScale);
this.offsetY -= imgY * (this.scale - oldScale);
// 重绘图像
this.drawImage();
},
handleMouseDown(e) {
this.isDragging = true;
const rect = this.canvas.getBoundingClientRect();
this.startX = e.clientX - this.offsetX;
this.startY = e.clientY - this.offsetY;
},
handleMouseMove(e) {
if (!this.isDragging) return;
const rect = this.canvas.getBoundingClientRect();
this.offsetX = e.clientX - this.startX;
this.offsetY = e.clientY - this.startY;
this.drawImage();
},
handleMouseUp() {
this.isDragging = false;
},
// 公共方法:重置视图
resetView() {
this.scale = this.initialScale;
this.offsetX = 0;
this.offsetY = 0;
this.drawImage();
}
},
beforeDestroy() {
window.removeEventListener('resize', this.resizeCanvas);
}
}
</script>
<style>
.canvas-container {
width: 500px;
height: 500px;
margin: 0 auto;
overflow: hidden;
position: relative;
}
canvas {
display: block;
background-color: #f0f0f0;
cursor: grab;
}
canvas:active {
cursor: grabbing;
}
</style>