原理:
-
触摸事件处理:
- 使用
@touchstart
、@touchmove
和@touchend
事件来监听用户的触摸操作。 - 在这些事件中更新图片的缩放和平移状态。
- 使用
-
触摸开始 (
onTouchStart
):- 当有两个触摸点时,初始化捏合操作的状态:
- 计算两个触摸点之间的初始距离
initialTouchDistance
。 - 记录当前的缩放级别
initialScale
。
- 计算两个触摸点之间的初始距离
- 当有一个触摸点时,记录初始触摸位置
lastTouchPosition
。
- 当有两个触摸点时,初始化捏合操作的状态:
-
触摸移动 (
onTouchMove
):- 平移操作:
- 计算手指移动的距离
dx
和dy
。 - 更新当前的平移量
currentTranslation
。
- 计算手指移动的距离
- 缩放操作:
- 计算两个触摸点之间的距离
touchDistance
。 - 根据新的触摸点距离和初始触摸点距离计算缩放比例
currentScale
。 - 限制缩放比例在 1 到 3 之间。
- 计算两个触摸点之间的距离
- 平移操作:
-
更新图片的变换状态 (
debouncedUpdateTransform
):- 使用
transform
属性更新图片的位置和缩放状态:
- 使用
效果如下:
移动端图片缩放、平移效果
代码如下:
<template>
<div>
<button @click="showModal">点我放大按钮</button>
<!-- 添加模态框 -->
<div class="modal" :class="{ 'is-active': isModalActive }">
<div class="modal-background" @click="hideModal"></div>
<img ref="zoomableImage" :src="currentImageUrl" alt="" @touchstart="onTouchStart" @touchmove="onTouchMove"
@touchend="onTouchEnd">
</div>
</div>
</template>
<script>
import url from './abc.png';
export default {
name: 'DataIndicator',
data() {
return {
// 模态框激活状态
isModalActive: false,
// 当前图像URL
currentImageUrl: '',
// 当前缩放级别
initialScale: 1,
// 初始触摸距离
initialTouchDistance: 0,
// 当前缩放比例
currentScale: 1,
// 是否开始捏合操作
pinchStart: false,
// 上次触摸位置
lastTouchPosition: null,
// 当前平移量
currentTranslation: { x: 0, y: 0 },
}
},
mounted() {
this.debouncedUpdateTransform(); // 初始化图片的transform状态
},
methods: {
showModal() {
this.currentImageUrl = url; // 设置当前图片URL,具体路径自己填写
this.currentScale = 3; // 初始化时设置图片放大3倍
this.debouncedUpdateTransform(); // 应用变换
this.isModalActive = true;
},
hideModal() {
this.isModalActive = false;
},
/**
* 处理触摸开始事件
* 此方法主要用于初始化触摸操作的起始状态,包括双指缩放和平移的初始状态
*
* @param {Event} e 触摸事件对象,包含触摸点的信息
*/
onTouchStart(e) {
// 当有两个触摸点时,初始化捏合操作的状态
if (e.touches.length === 2) {
this.pinchStart = true;
// 计算两个触摸点之间的初始距离
this.initialTouchDistance = Math.hypot(
e.touches[0].pageX - e.touches[1].pageX,
e.touches[0].pageY - e.touches[1].pageY
);
// 保存当前的缩放级别,以便在捏合操作中使用
this.initialScale = this.currentScale;
} else if (e.touches.length === 1) {
// 当有一个触摸点时,记录下初始触摸位置,用于平移操作
this.lastTouchPosition = {
x: e.touches[0].pageX,
y: e.touches[0].pageY,
};
}
},
/**
* 处理触摸移动事件
* @param {TouchEvent} e 触摸移动事件对象
*/
onTouchMove(e) {
// 如果没有处于捏合缩放状态且存在上一次触摸位置记录,则处理平移
if (!this.pinchStart && this.lastTouchPosition) {
// 计算手指移动的水平和垂直距离
const dx = e.touches[0].pageX - this.lastTouchPosition.x;
const dy = e.touches[0].pageY - this.lastTouchPosition.y;
// 更新当前的平移量,dx和dy代表手指移动的距离
// 注意这里除以6是为了减小移动的幅度,避免图片移动过快
this.currentTranslation = {
x: this.currentTranslation.x + dx / 6,
y: this.currentTranslation.y + dy / 6,
};
// 更新图片的变换状态
this.debouncedUpdateTransform();
}
// 如果处于捏合缩放状态,则处理缩放
else if (this.pinchStart) {
// 计算两个触摸点之间的距离
const touchDistance = Math.hypot(
e.touches[0].pageX - e.touches[1].pageX,
e.touches[0].pageY - e.touches[1].pageY
);
// 根据新的触摸点距离和初始触摸点距离计算缩放比例
this.currentScale = this.initialScale * (touchDistance / this.initialTouchDistance);
// 限制缩放比例在1到3之间
this.currentScale = Math.max(1, Math.min(3, this.currentScale));
// 更新图片的变换状态
this.debouncedUpdateTransform();
}
},
// 图片放大
debouncedUpdateTransform() {
const image = this.$refs.zoomableImage;
image.style.transform = `translate(${this.currentTranslation.x}px, ${this.currentTranslation.y}px) scale(${this.currentScale})`;
},
onTouchEnd() {
this.pinchStart = false;
},
}
}
</script>
<style>
img {
display: block;
/* 确保 img 是块级元素,这样设置宽度才有效 */
width: 100%;
/* 让 img 宽度填满其容器 */
height: 200px;
/* 自动调整高度以保持图片比例 */
/* border: 1px solid cadetblue; */
}
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
visibility: hidden;
opacity: 0;
transition: visibility 0s linear .25s, opacity .25s 0s;
z-index: 10000;
}
.modal.is-active {
visibility: visible;
opacity: 1;
}
.modal-background {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.8);
}
.modal-content {
position: relative;
width: 100%;
height: 60%;
overflow: auto;
background-color: white;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
img {
height: 100%;
/* 图片高度与模态框高度相同 */
object-fit: contain;
/* 保持图片比例,图片不会被拉伸 */
}
}
.modal-close {
position: absolute;
top: 10px;
right: 10px;
font-size: 24px;
color: #aaa;
cursor: pointer;
}
</style>