1.组件chargingimageViewer
<template>
<view class="image-view" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd">
<image
:src="imageUrl"
mode="aspectFill"
:style="{
width: imageWidth + 'rpx',
height: imageHeight + 'rpx',
transform: `translate(${currentX}rpx, ${currentY}rpx)`,
}"
></image>
</view>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue';
const props = defineProps({
imageUrl: {
type: String,
required: true,
},
});
// 视图容器尺寸
const VIEW_WIDTH = 750; // 微信小程序设计稿宽度
const VIEW_HEIGHT = 1800;
// 缩放限制
const MAX_SCALE = 5;
const MIN_SCALE = ref(1);
// 图片信息
const imageWidth = ref(0);
const imageHeight = ref(0);
const originalWidth = ref(0);
const originalHeight = ref(0);
const scale = ref(1);
const initialScale = ref(1);
// 拖动状态
const isDragging = ref(false);
const isPinching = ref(false);
const startX = ref(0);
const startY = ref(0);
const startDistance = ref(0);
const currentX = ref(0);
const currentY = ref(0);
const lastX = ref(0);
const lastY = ref(0);
const pivotX = ref(0);
const pivotY = ref(0);
// 计算图片按比例放大后的尺寸
const calculateImageSize = (width, height, scaleVal = 1.1) => {
// 计算宽高比
const ratio = width / height;
// 优先满足高度,因为容器高度更大
let newHeight = VIEW_HEIGHT;
let newWidth = newHeight * ratio;
// 如果宽度超出容器,则以宽度为基准
if (newWidth < VIEW_WIDTH) {
newWidth = VIEW_WIDTH;
newHeight = newWidth / ratio;
}
// 应用缩放
newWidth *= scaleVal;
newHeight *= scaleVal;
return {
width: newWidth,
height: newHeight,
};
};
// 获取图片原始尺寸
const getImageInfo = () => {
uni.getImageInfo({
src: props.imageUrl,
success: (res) => {
originalWidth.value = res.width;
originalHeight.value = res.height;
// 计算按比例放大后的图片尺寸
const size = calculateImageSize(res.width, res.height);
imageWidth.value = size.width;
imageHeight.value = size.height;
initialScale.value = size.width / res.width;
// 设置最小缩放比例
MIN_SCALE.value = initialScale.value;
scale.value = initialScale.value;
// 初始居中显示
currentX.value = (VIEW_WIDTH - size.width) / 2;
currentY.value = (VIEW_HEIGHT - size.height) / 2;
lastX.value = currentX.value;
lastY.value = currentY.value;
},
fail: (err) => {
console.error('获取图片信息失败', err);
},
});
};
// 限制图片拖动边界
const limitBounds = () => {
// 计算可拖动范围
const maxX = Math.max(0, (VIEW_WIDTH - imageWidth.value) / 2);
const minX = Math.min(0, VIEW_WIDTH - imageWidth.value - maxX);
const maxY = Math.max(0, (VIEW_HEIGHT - imageHeight.value) / 2);
const minY = Math.min(0, VIEW_HEIGHT - imageHeight.value - maxY);
// 限制X方向
if (currentX.value > maxX) {
currentX.value = maxX;
} else if (currentX.value < minX) {
currentX.value = minX;
}
// 限制Y方向
if (currentY.value > maxY) {
currentY.value = maxY;
} else if (currentY.value < minY) {
currentY.value = minY;
}
};
// 更新图片尺寸和位置
const updateImage = () => {
const size = calculateImageSize(originalWidth.value, originalHeight.value, scale.value);
imageWidth.value = size.width;
imageHeight.value = size.height;
// 调整位置以保持缩放中心不变
if (isPinching.value) {
currentX.value = pivotX.value - (pivotX.value - lastX.value) * (scale.value / lastScale.value);
currentY.value = pivotY.value - (pivotY.value - lastY.value) * (scale.value / lastScale.value);
}
// 限制边界
limitBounds();
};
let lastScale = 1;
// 触摸事件处理
const onTouchStart = (e) => {
if (e.touches.length === 1) {
// 单点触摸 - 拖动
isDragging.value = true;
isPinching.value = false;
startX.value = e.touches[0].clientX;
startY.value = e.touches[0].clientY;
} else if (e.touches.length === 2) {
// console.log(e.touches);
// // 双点触摸 - 缩放
// isDragging.value = false;
// isPinching.value = true;
// const touch1 = e.touches[0];
// const touch2 = e.touches[1];
// // 计算两点距离
// const dx = touch2.clientX - touch1.clientX;
// const dy = touch2.clientY - touch1.clientY;
// startDistance.value = Math.sqrt(dx * dx + dy * dy);
// // 计算缩放中心点
// pivotX.value = (touch1.clientX + touch2.clientX) / 2;
// pivotY.value = (touch1.clientY + touch2.clientY) / 2;
// // 记录当前状态
// lastScale = scale.value;
// lastX.value = currentX.value;
// lastY.value = currentY.value;
}
};
const onTouchMove = (e) => {
// 阻止事件冒泡和默认行为,防止页面滚动
// e.stopPropagation()
// e.preventDefault()
if (isDragging.value && e.touches.length === 1) {
// 处理拖动
const clientX = e.touches[0].clientX;
const clientY = e.touches[0].clientY;
// 计算移动距离
const deltaX = clientX - startX.value;
const deltaY = clientY - startY.value;
// 更新图片位置
currentX.value = lastX.value + deltaX;
currentY.value = lastY.value + deltaY;
// 限制边界
limitBounds();
} else if (isPinching.value && e.touches.length === 2) {
// // 处理缩放
// const touch1 = e.touches[0];
// const touch2 = e.touches[1];
// // 计算两点距离
// const dx = touch2.clientX - touch1.clientX;
// const dy = touch2.clientY - touch1.clientY;
// const distance = Math.sqrt(dx * dx + dy * dy);
// // 计算缩放比例
// const newScale = lastScale * (distance / startDistance.value);
// // 限制缩放范围
// if (newScale >= MIN_SCALE.value && newScale <= MAX_SCALE) {
// scale.value = newScale;
// updateImage();
// }
}
};
const onTouchEnd = () => {
// 阻止事件冒泡,防止页面滚动
// e.stopPropagation()
isDragging.value = false;
isPinching.value = false;
lastX.value = currentX.value;
lastY.value = currentY.value;
};
onMounted(() => {
getImageInfo();
});
</script>
<style scoped>
.image-view {
width: 750rpx;
height: 1800rpx;
overflow: hidden;
position: relative;
}
image {
position: absolute;
transition: transform 0s;
}
</style>
2.引用
<view class="mapbox">
<chargingimageViewer :imageUrl="imageUrl"></chargingimageViewer>
</view>
import chargingimageViewer from '@/components/chargingimageViewer/chargingimageViewer.vue';
const imageUrl = ref('../../static/images/dingwei.png');